2009-10-01 13 views

Respuesta

26

Es porque el valor flotante más cercano a 1.3 no es lo mismo que el valor doble más cercano a 1.3. Ninguno de los valores será exactamente 1.3 - que no se puede representar exactamente en una representación binaria no recurrente.

para dar una comprensión diferente de por qué sucede esto, supongamos que tenemos dos tipos decimal de coma flotante - decimal5 y decimal10, donde el número representa el número de dígitos significativos. Ahora supongamos que intentamos asignar el valor de "un tercio" a ambos. Usted terminaría con

decimal5 oneThird = 0.33333 
decimal10 oneThird = 0.3333333333 

Claramente esos valores no son iguales. Es exactamente lo mismo aquí, solo con diferentes bases involucradas.

Sin embargo, si se restringen los valores con el tipo menos precisa, encontrará que son igual en este caso particular:

double d = 1.3d; 
float f = 1.3f; 
System.out.println((float) d == f); // Prints true 

eso no es garantía de ser el caso, sin embargo. A veces, la aproximación del literal decimal a la representación doble, y luego la aproximación de ese valor a la representación flotante, termina siendo menos precisa que la aproximación decimal recta a flotante. Un ejemplo de esto es 1.0000001788139343 (gracias a stephentyrone por encontrar este ejemplo).

somewaht con mayor seguridad, se puede hacer la comparación entre dobles, pero utilizar un float literal en la asignación original:

double d = 1.3f; 
float f = 1.3f; 
System.out.println(d == f); // Prints true 

En este último caso, que es un poco como decir:

decimal10 oneThird = 0.3333300000 

Sin embargo, como se señaló en los comentarios, es casi seguro que no debe comparar comparando valores de punto flotante con ==. Es casi nunca lo que hay que hacer, precisamente por este tipo de cosas. Por lo general, si desea comparar dos valores, hágalo con algún tipo de comparación de igualdad "difusa", verificando si los dos números son "suficientemente cercanos" para sus propósitos. Consulte la página Java Traps: double para obtener más información.

Si realmente necesita verificar la igualdad absoluta, eso generalmente indica que debe utilizar un formato numérico diferente en primer lugar; por ejemplo, para los datos financieros, probablemente debería estar usando BigDecimal.

+0

Muy buena explicación, aunque es posible que un valor decimal cuidadosamente diseñado para fallar la comparación '((float) decimalValueAsDouble == decimalValueAsFloat)', debido a un doble redondeo. –

+0

@stephentyrone: Sí, sospechaba que podría serlo, aunque todavía no he encontrado ningún ejemplo. –

+1

Lo único que me preocupa con este post es que de alguna manera le da al OP la impresión de que puede usar yes == si no entiende los comentarios. Alguien más publicó este enlace: http://firstclassthoughts.co.uk/java/traps/java_double_traps.html, que lo explica bastante bien. – Fredrik

2

Nunca verifique la igualdad entre los números de coma flotante. Específicamente, para responder a su pregunta, el número 1.3 es difícil de representar en el punto flotante binario y las representaciones de almas y flotación son diferentes.

+0

Me hubiera obtenido un +1 si su publicación no estuviera llena de errores tipográficos. –

11

Un flotante es un número de coma flotante de precisión única. Un doble es un número de punto flotante de doble precisión.Más detalles aquí: http://www.concentric.net/~Ttwang/tech/javafloat.htm

Nota: Es una mala idea comprobar la igualdad exacta para los números de coma flotante. La mayoría de las veces, desea hacer una comparación basada en un valor de delta o tolerancia.

Por ejemplo:

float a = 1.3f; 
double b = 1.3; 
float delta = 0.000001f; 
if (Math.abs(a - b) < delta) 
{ 
    System.out.println("Close enough!"); 
} 
else 
{ 
    System.out.println("Not very close!"); 
} 

Algunos números no pueden representarse exactamente en el punto (por ejemplo 0.01) flotando por lo que podría obtener resultados inesperados cuando se compara por la igualdad.

2

Lee this article.

El artículo anterior ilustra claramente con ejemplos su escenario al usar tipos doble y flotante.

2
float a=1.3f; 
double b=1.3; 

En este momento tiene dos variables que contienen aproximaciones binarias al número Real 1.3. La primera aproximación tiene una precisión de aproximadamente 7 dígitos decimales, y la segunda es exacta a aproximadamente 15 dígitos decimales.

if(a==b) { 

La expresión a==b se evalúa en dos etapas. Primero se convierte el valor de a de float a double rellenando la representación binaria. El resultado sigue siendo exacto a aproximadamente 7 dígitos decimales como una representación del Real 1.3. Luego, compara las dos aproximaciones diferentes. Como son diferentes, el resultado de a==b es false.

Hay dos lecciones que aprender:

  1. punto flotante (dobles) y literales son casi siempre aproximaciones; p.ej. el número real que corresponde al literal 1.3f no es exactamente igual al número Real 1.3.

  2. Cada vez que realice un cálculo en coma flotante, los errores se infiltran. Estos errores tienden a acumularse. Entonces cuando comparas puntos flotantes/números dobles, generalmente es un error usar un simple "==", "<", etcétera. En su lugar, debe usar |a - b| < delta, donde delta se elige de forma adecuada. (Y averiguar lo que es una adecuada delta no siempre es directa, ya sea.)

  3. debería haber tomado ese curso en el análisis numérico :-)

+0

El problema, fundamentalmente, es que si bien hay muchos valores 'dobles' que un valor de 'flotación' particular podría representar, el sistema realiza una comparación' doble'-a-flotación' al seleccionar uno un valor 'doble' en particular. Si tuviera mi druthers, generalmente se permitirían conversiones implícitas de doble flotante y flotante a doble implícito generalmente prohibido, pero los operandos de coma flotante solo podrían compararse con tipos de concordancia precisa (algunos casos pueden requerir que ambos operandos pasen a ' float', pero otros, 'double'; ninguno es lo suficientemente dominante como para ser un valor predeterminado" seguro "). – supercat

0

El problema es que Java (y por desgracia .NET también) es inconsistente sobre si un valor float representa una única cantidad numérica precisa o un rango de cantidades. Si se considera que float representa una cantidad numérica exacta del formulario Mant * 2^Exp, donde Mant es un número entero 0 a 2^25 y Exp es un número entero), entonces un intento de convertir cualquier número que no sea de esa forma a float debería arrojar una excepción .Si se considera que representa "el lugar geométrico de los números para los cuales alguna representación particular en la forma anterior se considera que es la mejor", entonces un reparto doble flotante sería correcto incluso para los valores double que no sean de la forma anterior [ emitir el double que mejor represente una cantidad a float casi siempre arrojará el float que mejor represente esa cantidad, aunque en algunos casos de esquina (por ejemplo, cantidades numéricas en el rango 8888888.500000000001 a 8888888.500000000932) el float elegido puede ser de algunas partes por billones peor que la mejor representación posible float de la cantidad numérica real].

Para usar una analogía, supongamos que dos personas tienen un objeto de diez centímetros de largo y lo miden. Bob usa un conjunto costoso de calibres y determina que su objeto tiene 3.937008 "de largo. Joe usa una cinta métrica y determina que su objeto mide 3 15/16" de largo. Son los objetos del mismo tamaño? Si uno convierte la medida de Joe en millonésimas de pulgada (3.937500 "), las medidas parecerán diferentes, pero una convierte la medida de Bob a la fracción de 1/256 más cercana, aparecerán iguales. Aunque la primera comparación podría parecer más "precisa", la última tiende a ser más significativa. La medida de Joe si 3 15/16 "realmente no significa 3.937500" - significa "una distancia que, usando una cinta métrica, es indistinguible de 3 15/16". Y 3.937008 "es, como el tamaño del objeto de Joe, una distancia que con una cinta métrica sería indistinguible de 3 15/16.

Desafortunadamente, aunque sería más significativo comparar las mediciones con la menor precisión, Las reglas de comparación de coma flotante de Java suponen que un float representa una cantidad numérica precisa única y realiza comparaciones sobre esa base. Si bien hay algunos casos en que esto es útil (por ejemplo, saber si el particular double producido al emitir algún valor a float y viceversa) a double coincidiría con el valor de inicio), en general, las comparaciones directas de igualdad entre float y double no son significativas. Aunque Java no lo requiere, siempre se deben emitir los operandos de un flotante p una comparación de igualdad de igualdad para ser del mismo tipo. La semántica que resulta de emitir el double a float antes de la comparación es diferente de la conversión del float al double, y el comportamiento que Java selecciona de forma predeterminada (envía el float al double) a menudo es semánticamente incorrecto.

Cuestiones relacionadas