game float error arithmetic floating-point precision

floating-point - error - ieee float point



¿Por qué veo una variable doble inicializada a algún valor como 21.4 como 21.399999618530273? (14)

double r = 11.631; double theta = 21.4;

En el depurador, estos se muestran como 11.631000000000000 y 21.399999618530273 .

¿Cómo puedo evitar esto?



De acuerdo con el javadoc

"Si al menos uno de los operandos de un operador numérico es de tipo doble, entonces el
la operación se lleva a cabo utilizando una aritmética de punto flotante de 64 bits, y el resultado de la
operador numérico es un valor de tipo double. Si el otro operando no es un doble, es
primero se amplió (§5.1.5) para escribir el doble por promoción numérica (§5.6). "

Aquí está la fuente


Esto es en parte específico de la plataforma, y ​​no sabemos qué plataforma está usando.

También es en parte un caso de saber lo que realmente quieres ver. El depurador le muestra, en cierta medida, de todos modos, el valor preciso almacenado en su variable. En mi artículo sobre números binarios en coma flotante en .NET , hay una clase C # que te permite ver el número absolutamente exacto almacenado en un doble. La versión en línea no funciona en este momento. Trataré de poner una en otro sitio.

Dado que el depurador ve el valor "real", tiene que hacer una llamada de juicio sobre qué mostrar, puede mostrarle el valor redondeado a algunos lugares decimales o un valor más preciso. Algunos depuradores hacen un mejor trabajo que otros al leer las mentes de los desarrolladores, pero es un problema fundamental con los números de coma flotante binarios.


Estos problemas de precisión se deben a la representación interna de los números de punto flotante y no hay mucho que pueda hacer para evitarlo.

Por cierto, imprimir estos valores en tiempo de ejecución a menudo todavía conduce a los resultados correctos, al menos utilizando compiladores modernos de C ++. Para la mayoría de las operaciones, esto no es un gran problema.


Me gustó la explicación de Joel , que trata sobre un problema de precisión de punto flotante binario similar en Excel 2007:

¿Ves que hay un gran número 0110 0110 0110 allí al final? Eso es porque 0.1 no tiene representación exacta en binario ... es un número binario repetitivo. Es algo así como que 1/3 no tiene representación en decimal. 1/3 es 0.33333333 y tienes que seguir escribiendo 3 para siempre. Si pierdes la paciencia, obtienes algo inexacto.

Así que puedes imaginar cómo, en decimal, si intentaste hacer 3 * 1/3, y no tuviste tiempo para escribir 3 para siempre, el resultado que obtendrías sería 0.99999999, no 1, y la gente se enojaría con por estar equivocado


Me he encontrado con esto antes ( en mi blog ) - Creo que la sorpresa suele ser que los números "irracionales" son diferentes.

Por ''irracional'' aquí solo me refiero al hecho de que no se pueden representar con precisión en este formato. Los números irracionales reales (como π - pi) no se pueden representar con exactitud en absoluto.

La mayoría de las personas están familiarizadas con que 1/3 no funciona en decimal: 0.3333333333333 ...

Lo curioso es que 1.1 no funciona en flotadores. La gente espera que los valores decimales funcionen en números flotantes debido a cómo piensan en ellos:

1.1 es 11 x 10 ^ -1

Cuando en realidad están en la base-2

1.1 es 154811237190861 x 2 ^ -47

No puedes evitarlo, solo tienes que acostumbrarte al hecho de que algunas carrozas son "irracionales", de la misma manera que 1/3.


Me parece que 21.399999618530273 es la representación de precisión única (flotante) de 21.4. Parece que el depurador está bajando desde el doble para flotar en algún lugar.


No puede evitar esto ya que está usando números de coma flotante con una cantidad fija de bytes. Simplemente no hay isomorfismo posible entre los números reales y su notación limitada.

Pero la mayoría de las veces puedes simplemente ignorarlo. 21.4 == 21.4 seguiría siendo cierto porque sigue siendo los mismos números con el mismo error. Pero 21.4f == 21.4 puede no ser cierto porque el error para float y double es diferente.

Si necesita una precisión fija, quizás deba probar los números de punto fijo. O incluso enteros. Yo, por ejemplo, a menudo uso int (1000 * x) para pasar al depurador.


Si está utilizando Java y necesita precisión, use la clase BigDecimal para cálculos de coma flotante. Es más lento pero más seguro.



Si tiene un valor como:

double theta = 21.4;

Y quieres hacer:

if (theta == 21.4) { }

Tienes que ser un poco inteligente, deberás verificar si el valor de theta es realmente cercano a 21.4, pero no necesariamente ese valor.

if (fabs(theta - 21.4) <= 1e-6) { }


Una forma de evitar esto es utilizar una biblioteca que use un método alternativo para representar números decimales, como BCD


Use el tipo decimal punto fijo si desea estabilidad en los límites de precisión. Hay gastos generales, y debe emitir explícitamente si desea convertir a punto flotante. Si lo convierte a punto flotante volverá a introducir las inestabilidades que parecen molestarlo.

Alternativamente, puede superarlo y aprender a trabajar con la precisión limitada de la aritmética de coma flotante. Por ejemplo, puede usar el redondeo para hacer que los valores converjan, o puede usar comparaciones épsilon para describir una tolerancia. "Epsilon" es una constante que configura que define una tolerancia. Por ejemplo, puede optar por considerar que dos valores son iguales si están dentro de 0,0001 entre sí.

Se me ocurre que puede usar la sobrecarga del operador para hacer que las comparaciones épsilon sean transparentes. Eso sería genial.

Para las representaciones de exponente de mantisa EPSILON debe calcularse para permanecer dentro de la precisión representable. Para un número N, Epsilon = N / 10E + 14

System.Double.Epsilon es el valor positivo representable más pequeño para el tipo Double . Es demasiado pequeño para nuestro propósito. Lea el consejo de Microsoft sobre pruebas de igualdad