c++ - round - 0.1 float es mayor que 0.1 double. Esperaba que fuera falso
round float c++ to 2 decimal places (7)
Esta pregunta ya tiene una respuesta aquí:
Dejar:
double d = 0.1;
float f = 0.1;
debería la expresión
(f > d)
devolver true
o false
?
Empíricamente, la respuesta es true
. Sin embargo, esperaba que fuera false
.
Como 0.1
no se puede representar perfectamente en binario, mientras que el doble tiene de 15
a 16
dígitos decimales de precisión, y el flotante tiene solo 7
. Entonces, ambos son menos de 0.1
, mientras que el doble está más cerca de 0.1
.
Necesito una explicación exacta para la true
.
Como no se puede representar exactamente, comparar 1/10 en la base 2 es como comparar 1/7 en la base 10.
1/7 = 0.142857142857 ... pero comparando con diferentes precisiones de la base 10 (3 decimales) tenemos 0.143> 0.142857.
Creo que el comentario de Eric Lippert sobre la pregunta es en realidad la explicación más clara, así que lo volveré a publicar como una respuesta:
Supongamos que está calculando 1/9 en decimal de 3 dígitos y decimal de 6 dígitos. 0.111 <0.111111, ¿verdad?
Ahora suponga que está calculando 6/9. 0.667> 0.666667, ¿verdad?
¡No se puede tener que 6/9 en decimal de tres dígitos es 0.666 porque ese no es el decimal de 3 dígitos más cercano a 6/9!
El número 0.1 se redondeará a la representación de coma flotante más cercana con la precisión dada. Esta aproximación puede ser mayor o menor que 0,1, por lo que sin mirar los valores reales, no se puede predecir si la precisión simple o la aproximación de doble precisión es mayor.
Aquí es a lo que se redondea el valor de doble precisión (usando un intérprete de Python):
>>> "%.55f" % 0.1
''0.1000000000000000055511151231257827021181583404541015625''
Y aquí está el único valor de precisión:
>>> "%.55f" % numpy.float32("0.1")
''0.1000000014901161193847656250000000000000000000000000000''
Entonces puede ver que la aproximación de precisión simple es mayor.
El rango de doble es mayor que el de flotación en las conversiones. Al hacer una comparación lógica, f se convierte en doble y tal vez la implementación que está utilizando arroje resultados inconsistentes. Si sufijas f para que el compilador lo registre como un flotante, obtienes 0.00 que es falso en el tipo doble. Los tipos flotantes Unsuffixed son dobles.
#include <stdio.h>
#include <float.h>
int main()
{
double d = 0.1;
float f = 0.1f;
printf("%f/n", (f > d));
return 0;
}
Si convierte .1
en binario obtendrá:
0.000110011001100110011001100110011001100110011001100...
repitiendo por siempre
Al mapear tipos de datos, obtienes:
float(.1) = %.00011001100110011001101 ^--- note rounding double(.1) = %.0001100110011001100110011001100110011001100110011010
Convierta eso en base 10:
float(.1) = .10000002384185791015625 double(.1) = .100000000000000088817841970012523233890533447265625
Esto fue tomado de un artículo escrito por Bruce Dawson. Se puede encontrar aquí:
Los dobles no son flotadores, así que no los compare
Solo para agregar a las otras respuestas hablando de IEEE-754 y x86: el problema es aún más complicado de lo que parece. No hay una "representación" de 0.1 en IEEE-754; hay dos. O redondear el último dígito hacia abajo o hacia arriba sería válido. Esta diferencia puede ocurrir y de hecho ocurre porque x86 no usa 64 bits para sus cálculos internos de coma flotante; ¡en realidad usa 80 bits! Esto se llama precisión extendida doble .
Entonces, incluso entre compiladores x86, a veces ocurre que el mismo número se representa de dos maneras diferentes, porque algunos computan su representación binaria con 64 bits, mientras que otros usan 80.
De hecho, puede suceder incluso con el mismo compilador, ¡incluso en la misma máquina!
#include <iostream>
#include <cmath>
void foo(double x, double y)
{
if (std::cos(x) != std::cos(y)) {
std::cout << "Huh?!?/n"; //← you might end up here when x == y!!
}
}
int main()
{
foo(1.0, 1.0);
return 0;
}
Ver ¿Por qué es cos(x) != cos(y)
aunque x == y
? para más información.
Yo diría que la respuesta depende del modo de redondeo al convertir el double
en float
. float
tiene 24 bits binarios de precisión, y el double
tiene 53. En binario, 0.1 es:
0.1₁₀ = 0.0001100110011001100110011001100110011001100110011…₂
^ ^ ^ ^
1 10 20 24
Si redondeamos al 24º dígito, obtendremos
0.1₁₀ ~ 0.000110011001100110011001101
que es mayor que el valor exacto y la aproximación más precisa a 53 dígitos.