在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 =精确,与/ fp = strict一样,/ fp = fast。
问题出在这里?任何提示如何避免这个问题?这导致严重的数字问题。
答案 0 :(得分:4)
这不是VC ++或类似的问题 - 这是浮点数如何存储在计算机上的基本问题。有关详细信息,请参阅IEEE-754。
问题是从float转换为double,以便从double转换回float会导致与您开始时完全相同的浮点值。除了在需要更长的精度时仅使用双精度,我不知道有任何方法可以减少精度。尝试将round
转换后的浮点数设置为两位小数可能会将其设置为正确的值,但我不确定。
答案 1 :(得分:3)
f1
中的值和d2
中的值都代表完全相同的数字。这个数字不完全是42.480000,也不是42.479999542236328,尽管它确实有一个终止的十进制表示。当显示浮动时,调试视图在浮点数的精度上合理地舍入,并且当显示双精度时,它以双精度的精度舍入。因此,当您转换并显示为双倍时,您会看到神秘值的两倍。
d1
包含比神秘值更好的近似值4.48,因为d1
包含最接近4.48的双倍,而f1
和d2
仅包含最接近的浮点值到4.48。你期望d2
包含什么? f1不能“记住”它“真的应该是”4.48,所以当它转换为double时,它会变得“更准确”。
避免它的方法取决于你指的是哪个严重的数字问题。如果问题是d1和d2不相等,并且您认为它们应该相等,那么答案是在比较中包含一个小容差,例如,将d1 == d2
替换为:
fabs(d1 - d2) <= (d2 * FLT_EPSILON)
这只是一个例子,但我没有检查它是否涉及这种情况。您必须选择适合您的容差,并且您可能还需要担心许多边缘情况 - d2可能为零,值可能是无穷大或NaN,可能是其他值。
如果问题是d2不是足够准确的算法值来产生准确的结果,那么你必须避免使用float
值,和/或使用更加数字稳定的算法。
答案 2 :(得分:2)
这里发生的事情没有错。
由于浮点数在内存中的表示方式,42.479999999999997是双精度数最接近的42.48。
阅读本文: http://docs.sun.com/source/806-3568/ncg_goldberg.html
它解释了那里发生的事情。遗憾的是,你无法对其进行存储。