错误的Visual C浮点数/双精度转换?

3
在Visual C++中,我编写了以下在C++程序中的示例:
float f1 = 42.48f;
double d1 = 42.48;
double d2 = f1;

我使用Visual Studio 2005编译了这个程序。 在调试器中,我看到以下数值:

f1  42.480000   float
d1  42.479999999999997  double
d2  42.479999542236328  double

d1在我的认知中没有问题,但d2是有问题的。

使用/fp=precise、/fp=strict或/fp=fast都会出现这个问题。

问题在哪里?有什么提示可以避免这个问题吗?这会导致严重的数值问题。

3个回答

4
这并不是VC++或其他任何东西的问题 - 这是关于计算机上如何存储浮点数的根本问题。更多信息,请参见IEEE-754
问题在于将float转换为double时,会进行一种转换,使得从double转换回float会得到与开始时完全相同的float值。我不知道有什么办法可以避免精度损失,除非在需要更长精度时只使用doubles。可能尝试将转换后的float四舍五入到两个小数位会将其设置为正确的值,但我不确定。

3
f1d2中的值都代表同一个数字,这个数字不是完全等于42.480000,也不是完全等于42.479999542236328,但它有一个十进制表示法。当显示浮点数时,您的调试视图会合理地在浮点精度处四舍五入;当显示双精度浮点数时,会在双精度精度处四舍五入。因此,将其转换并显示为双精度浮点数时,您会看到关于神秘值的有效数字大约多两倍。 d1包含比神秘值更接近4.48的近似值,因为d1包含最接近4.48的双精度浮点数,而f1d2只包含最接近4.48的浮点数值。您期望d2包含什么?f1不能“记住”它“实际上应该是”4.48,所以当它转换为双精度浮点数时就变得“更准确”。
避免这种情况的方法取决于您指的是哪些严重的数值问题。如果问题是d1和d2不相等,而您认为它们应该相等,则答案是在比较中包含一个小容差,例如,将d1 == d2替换为:
fabs(d1 - d2) <= (d2 * FLT_EPSILON)

这只是一个例子,我没有检查它是否涵盖了这种情况。您需要选择适合您的公差,并且您可能还需要担心很多边缘情况--d2可能为零,任一值可能为无穷大或NaN,还可能有其他情况。

如果问题在于d2不是足够准确的值,以便您的算法产生准确的结果,则必须避免使用float值,并/或使用更稳定的数值算法。


2

这里发生的事情没有任何问题。

由于浮点数在内存中的表示方式,42.479999999999997是双精度浮点数最接近42.48的表示。

阅读此文档:http://docs.sun.com/source/806-3568/ncg_goldberg.html

它解释了发生的情况。不幸的是,你无法对其存储做任何更改。


+1 链接到《计算机科学家应该了解的浮点运算知识》。 - Paul R

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接