¿Por qué veo una variable doble inicializada en algún valor como 21.4 como 21.399999618530273?

StackOverflow https://stackoverflow.com/questions/177506

  •  05-07-2019
  •  | 
  •  

Pregunta

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?

¿Fue útil?

Solución

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

Por cierto, la impresión de 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.

Otros consejos

Me gustó Explicación de Joel , que trata sobre una flotación binaria similar cuestión de precisión de puntos en Excel 2007:

  

¿Ves cómo hay un montón de 0110 0110 0110 al final? Esto se debe a que 0.1 no tiene representación exacta en binario ... es un número binario que se repite. 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 intentas 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 enojarse contigo por estar equivocado.

Si tiene un valor como:

double theta = 21.4;

Y quieres hacer:

if (theta == 21.4)
{
}

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

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

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

En parte, también se trata de saber lo que realmente desea ver. El depurador le está mostrando, hasta cierto punto, de todos modos, el valor preciso almacenado en su variable. En mi artículo sobre números de punto flotante binario en .NET , hay un C # class que le permite ver el número absolutamente exacto almacenado en un doble. La versión en línea no funciona en este momento; intentaré poner una en otro sitio.

Dado que el depurador ve el " real " valor, tiene que emitir un juicio acerca de qué mostrar: podría mostrar el valor redondeado a unos pocos decimales, o un valor más preciso. Algunos depuradores hacen un mejor trabajo que otros al leer la mente de los desarrolladores, pero es un problema fundamental con los números binarios de punto flotante.

Use el tipo decimal de 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 te conviertes en punto flotante, reintroducirás las inestabilidades que parecen molestarte.

Alternativamente, puede superarlo y aprender a trabajar con la precisión limitada de la aritmética de punto flotante. Por ejemplo, puede usar el redondeo para hacer que los valores converjan, o puede usar comparaciones de epsilon para describir una tolerancia. " Epsilon " Es una constante que configuras que define una tolerancia. Por ejemplo, puede elegir considerar dos valores como iguales si están a 0,0001 de cada uno.

Se me ocurre que podría usar la sobrecarga de operadores para hacer transparentes las comparaciones de épsilon. Eso sería muy bueno.


Para las representaciones de la mantisa-exponente, EPSILON debe calcularse para que permanezca 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 Consejos de Microsoft sobre pruebas de igualdad

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í me refiero al hecho de que no se pueden representar con precisión en este formato. Los números reales irracionales (como p - pi) no se pueden representar con precisión en absoluto.

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

Lo extraño es que 1.1 no funciona en flotadores. La gente espera que los valores decimales funcionen en números de punto flotante debido a la forma en que piensan de ellos:

  

1.1 es 11 x 10 ^ -1

Cuando en realidad están en base-2

  

1.1 es 154811237190861 x 2 ^ -47

No puedes evitarlo, solo debes acostumbrarte al hecho de que algunos flotadores son 'irracionales', de la misma manera que 1/3 es.

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

Me parece que 21.399999618530273 es la representación de precisión simple (flotante) de 21.4. Parece que el depurador está cambiando de doble a flotar en alguna parte.

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

No puedes evitar esto porque estás usando números de punto 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 simplemente puedes ignorarlo. 21.4 == 21.4 todavía sería 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 precisión fija, tal vez debería intentar números de punto fijo. O incluso enteros. Por ejemplo, a menudo uso int (1000 * x) para pasar al depurador de paginación.

Si le molesta, puede personalizar la forma en que se muestran algunos valores durante la depuración. Úsalo con cuidado :-)

Mejorando la depuración con los atributos de visualización del depurador

Consulte Aritmética decimal general

También tome nota cuando compare flotantes, consulte esta respuesta para más información.

Según el javadoc

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

Aquí está la fuente

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top