Ver Mensaje Individual
  #11 (permalink)  
Antiguo 04/09/2015, 20:59
agleiva
(Desactivado)
 
Fecha de Ingreso: enero-2015
Mensajes: 393
Antigüedad: 9 años, 3 meses
Puntos: 52
Respuesta: Problema con decimales.

Ok, te tiro un par de sogas:

Logré reproducir tu problema en .Net 4.5.2 con F# 4.0. Básicamente, como te indicaron arriba, es un problema muy común en casi todos los lenguajes de programación, debido a la forma en la que el CPU hace los cálculos de punto flotante.

El siguiente código F#:

Código F#:
Ver original
  1. open System
  2.  
  3. [<EntryPoint>]
  4. let main argv =
  5.     let mutable i = 0.0
  6.     while i < 0.9 do
  7.         i <- i + 0.1
  8.         Console.WriteLine(i)
  9.     Console.ReadLine() |> ignore
  10.     0

devuelve este resultado:

0,10000000000000001
0,20000000000000001
0,30000000000000004
0,40000000000000002
0,5
0,59999999999999998
0,69999999999999996
0,79999999999999993
0,89999999999999991
0,99999999999999989

Ahí se pueden ver claramente los errores de precisión, y la causa del problema: al llegar a la 9na iteración, se está comparando el valor 0.89999999999999991, el cuál es menor a 9, con lo cuál el código se ejecuta una vez más, llegando a 0,99999999999999989.

Googleando rápidamente (otra habilidad muy necesaria para ser programador), encontré esto:

http://stackoverflow.com/questions/6...541929#1541929

Cita:
Iniciado por Dicho sea de paso
Nótese que nunca jamás he usado ActionScript en mi vida, y aún así pude encontrar una solución a este problema. Esas son las cosas que necesitás practicar.
En ese post hay una sugerencia:

Código ActionScript:
Ver original
  1. var setPrecision:Function = function(number:Number, precision:int) {
  2.  precision = Math.pow(10, precision);
  3.  return Math.round(number * precision)/precision;
  4. }

es una función que redondea el valor que le pases en el primer parámetro, a la cantidad de decimales que le pases segundo parámetro, por ejemplo:

Código ActionScript:
Ver original
  1. var number:Number = 10.98813311;
  2. trace(setPrecision(number,1)); //Result is 10.9
  3. trace(setPrecision(number,2)); //Result is 10.98
  4. trace(setPrecision(number,3)); //Result is 10.988 and so on

Hay otros comentarios, por ejemplo uno que dice que esta misma función fue incorporada en ActionScript 3.0, con lo cuál deberías poder usarlo, si al menos estuvieras en una versión más actualizada de esa plataforma.

Después vi esto: https://code.google.com/p/bigdecimal/ que básicamente es un port a ActionScript del tipo BigDecimal de java, pero me llamó muchísimo el comentario del tipo:

Cita:
So for all of you in financials, NEVER USE FLEX OR FLASH to compute data. Seriously guys. DONT use that technology. It is the worst thing that ever existed in the world.
Que traducido es algo así como:

Cita:
Así que para todos lo que estén en finanzas, NUNCA USEN FLEX NI FLASH para computar daros. En serio muchachos. NO usen esa technología. Es lo peor que alguna vez existió en el mundo.
Seguido de un montón de otras quejas de deficiencias en esa plataforma, que no vienen al caso...

Curioso no?

Nótese que en las plataformas serias hay implementaciones de tipos numéricos decimales (decimal en .Net, BigDecimal en java) que no tienen este problema ya que están pensados justamente para representar números decimales.

Por ejemplo, el problema se soluciona en .Net usando el tipo decimal, cambiando los literales numéricos, usando la "m" que es el sufijo que indica tipos decimales en .Net:

Código F#:
Ver original
  1. let mutable i = 0m
  2.     while i < 0.9m do
  3.         i <- i + 0.1m

Y por supuesto ya que estamos usando F# podemos reemplazar ese while horrible y procedural por un for sobre un literal de lista, quitar la variable mutable, y cambiar el Console.WriteLine por printf:

Código F#:
Ver original
  1. open System
  2.  
  3. [<EntryPoint>]
  4. let main argv =
  5.     for i in 0m .. 0.1m .. 0.9m do
  6.         printf "%M " i
  7.     Console.ReadLine() |> ignore
  8.     0

O incluso algo mucho más idiomático y propio de un lenguaje funcional:

Código F#:
Ver original
  1. open System
  2.  
  3. [<EntryPoint>]
  4. let main argv =
  5.     let print(x) = printf "%M " x
  6.     [0m .. 0.1m .. 0.9m]
  7.     |> Seq.map(print) |> ignore
  8.     Console.ReadLine() |> ignore
  9.     0

Última edición por agleiva; 04/09/2015 a las 22:01