17

问题。

Microsoft Visual C++ 2005 编译器,32 位 windows xp sp3,amd 64 x2 cpu。

代码:

double a = 3015.0; 
double b = 0.00025298219406977296;
//*((unsigned __int64*)(&a)) == 0x40a78e0000000000  
//*((unsigned __int64*)(&b)) == 0x3f30945640000000  
double f = a/b;//3015/0.00025298219406977296;

计算的结果(即“f”)是 11917835.000000000 ( ((unsigned __int64 )(&f)) == 0x4166bb4160000000) 虽然它应该是 11917834.814763514 (即((unsigned __int64 )(&f)) == 0x4166bb415a128a)
即小数部分丢失。
不幸的是,我需要小数部分才能正确。

问题:
1)为什么会发生这种情况?
2)我该如何解决这个问题?

附加信息:
0)结果直接来自“观察”窗口(没有打印,我没有忘记设置打印精度)。我还提供了浮点变量的十六进制转储,所以我对计算结果非常确定。
1) f = a/b的反汇编为:

fld         qword ptr [a]  
fdiv        qword ptr [b]  
fstp        qword ptr [f]  

2) f = 3015/0.00025298219406977296;产生正确的结果(f == 11917834.814763514 , ((unsigned __int64 )(&f)) == 0x4166bb415a128aef ),但看起来在这种情况下,结果只是在编译时计算:

fld         qword ptr [__real@4166bb415a128aef (828EA0h)]  
fstp        qword ptr [f]  

那么,我该如何解决这个问题呢?

PS我找到了一个临时解决方法(我只需要除法的小数部分,所以我现在只使用 f = fmod(a/b)/b ),但我仍然想知道如何正确解决这个问题 -双精度应该是 16 位十进制数字,所以这样的计算不应该引起问题。

4

5 回答 5

15

Are you using directx in your program anywhere as that causes the floating point unit to get switched to single precision mode unless you specifically tell it not to when you create the device and would cause exactly this

于 2010-03-28T18:16:55.020 回答
4

有趣的是,如果您将 a 和 b 都声明为浮点数,您将得到准确的 11917835.000000000。所以我的猜测是,在某处发生了向单精度的转换,无论是在如何解释常量或稍后在计算中。

不过,考虑到您的代码有多简单,这两种情况都有点令人惊讶。您没有使用任何奇异的编译器指令,强制所有浮点数使用单精度?

编辑:您是否确实确认编译的程序会产生不正确的结果?否则,(错误的)单精度转换最可能的候选者将是调试器。

于 2010-03-28T17:47:04.390 回答
2

如果您需要精确的数学,请不要使用浮点数。

帮自己一个忙,获得一个支持有理数的 BigNum 库。

于 2010-03-28T17:47:33.500 回答
1

我猜你在没有指定精度的情况下打印出数字。尝试这个:

#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;
}

这会产生:

11917834.814763514000000

这对我来说看起来是正确的。我使用的是 VC++ 2008 而不是 2005,但我猜区别在于您的代码,而不是编译器。

于 2010-03-28T17:13:05.420 回答
0

你确定你在 fstp 指令之后检查 f 的值吗?如果您启用了优化,那么监视窗口可能会显示稍后某个时候的值(这似乎有点合理,因为您说您稍后会查看 f 的小数部分 - 是否有一些指令最终掩盖了它以某种方式出来?)

于 2010-03-28T17:58:24.783 回答