c++ - double precision floating point
c++ pérdida de precisión de punto flotante: 3015/0.00025298219406977296 (5)
¿Está seguro de que está examinando el valor de f justo después de la instrucción fstp? Si tiene activadas las optimizaciones, quizás la ventana del reloj muestre un valor tomado en algún momento posterior (esto parece un poco plausible cuando dice que está viendo la parte fraccionaria de f más adelante; de alguna manera?)
El problema.
Compilador de Microsoft Visual C ++ 2005, 32 bit windows xp sp3, y 64 x2 cpu.
Código:
double a = 3015.0;
double b = 0.00025298219406977296;
//*((unsigned __int64*)(&a)) == 0x40a78e0000000000
//*((unsigned __int64*)(&b)) == 0x3f30945640000000
double f = a/b;//3015/0.00025298219406977296;
el resultado del cálculo (es decir, "f") es 11917835.000000000 ( ((sin signo __int64 ) (& f)) == 0x4166bb4160000000) aunque debería ser 11917834.814763514 (es decir ((sin signo __int64 ) (& f)) == 0x4166bb415a128)
Es decir, se pierde parte fraccionaria.
Desafortunadamente, necesito una parte fraccionaria para ser correcta.
Preguntas:
1) ¿Por qué sucede esto?
2) ¿Cómo puedo solucionar el problema?
Información adicional:
0) El resultado se toma directamente de la ventana "ver" (no se imprimió, y no me olvidé de configurar la precisión de impresión). También proporcioné el volcado hexadecimal de la variable de punto flotante, así que estoy absolutamente seguro del resultado del cálculo.
1) El desmontaje de f = a / b es:
fld qword ptr [a]
fdiv qword ptr [b]
fstp qword ptr [f]
2) f = 3015 / 0,00025298219406977296; produce el resultado correcto (f == 11917834.814763514, ((sin signo __int64 ) (& f)) == 0x4166bb415a128aef), pero parece que en este caso el resultado simplemente se calcula durante el tiempo de compilación:
fld qword ptr [__real@4166bb415a128aef (828EA0h)]
fstp qword ptr [f]
Entonces, ¿cómo puedo solucionar este problema?
PD: encontré una solución temporal temporal (solo necesito parte fraccionaria de la división, por lo que simplemente uso f = fmod (a / b) / b en este momento), pero aún me gustaría saber cómo solucionar este problema correctamente. se supone que la precisión doble es de 16 dígitos decimales, por lo que no se supone que este cálculo cause problemas.
¿Está utilizando directx en su programa en cualquier lugar, ya que esto hace que la unidad de punto flotante cambie a modo de precisión simple, a menos que usted lo indique específicamente cuando cree el dispositivo y causaría exactamente esto?
Curiosamente, si declara tanto a como b como flotadores, obtendrá exactamente 11917835.000000000. Así que mi conjetura es que hay una conversión a precisión simple que ocurre en alguna parte, ya sea en la forma en que se interpretan las constantes o más adelante en los cálculos.
Sin embargo, cualquiera de los dos casos es un poco sorprendente, considerando lo sencillo que es su código. ¿No está utilizando directivas de compilación exóticas, forzando una precisión única para todos los números de punto flotante?
Edición: ¿Has confirmado realmente que el programa compilado genera un resultado incorrecto? De lo contrario, el candidato más probable para la conversión de precisión simple (errónea) sería el depurador.
Supongo que está imprimiendo el número sin especificar una precisión. Prueba esto:
#include <iostream>
#include <iomanip>
int main() {
double a = 3015.0;
double b = 0.00025298219406977296;
double f = a/b;
std::cout << std::fixed << std::setprecision(15) << f << std::endl;
return 0;
}
Esto produce:
11917834.814763514000000
Lo que me parece correcto. Estoy usando VC ++ 2008 en lugar de 2005, pero supongo que la diferencia está en su código, no en el compilador.
Si necesitas cálculos precisos, no uses coma flotante.
Hágase un favor y obtenga una biblioteca BigNum con soporte de números racionales.