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 一样,也会出现问题。

这里有什么问题?任何提示如何避免这个问题?这会导致严重的数值问题。

4

3 回答 3

4

这不是 VC++ 或类似的问题 - 这是浮点数如何存储在计算机上的基本问题。有关详细信息,请参阅IEEE-754

问题是从 float 到 double 的转换是这样完成的,从 double 转换回 float 会产生与您开始时完全相同的 float 值。我不知道任何解决精度损失的方法,除了在需要更长的精度时只使用双精度。可能是尝试round将转换后的浮点数设置为两位小数会将其设置为正确的值,但我不确定。

于 2010-04-07T09:08:06.067 回答
3

中的值f1和中的值d2都表示完全相同的数字。该数字不完全是 42.480000,也不完全是 42.479999542236328,尽管它确实具有终止的十进制表示。显示浮点数时,您的调试视图会以浮点数的精度舍入,而当显示双精度时,它会以双精度数舍入。因此,当您转换并显示为双精度时,您会看到神秘值的有效数字的两倍。

d1包含比神秘值更好的 4.48 近似值,因为d1它包含最接近 4.48 的双精度值,而f1d2包含最接近 4.48 的浮点值。你期望d2包含什么?f1 无法“记住”它“真的应该是”4.48,因此当它转换为 double 时,它​​会变得“更准确”。

避免它的方法取决于您指的是哪些严重的数值问题。如果问题是 d1 和 d2 比较不相等,而您认为它们应该相等,那么答案是在比较中包含一个小的容差,例如,替换d1 == d2为:

fabs(d1 - d2) <= (d2 * FLT_EPSILON)

不过,这只是一个例子,我没有检查它是否处理这种情况。您必须选择一个适合您的容差,并且您可能还需要担心许多边缘情况——d2 可能为零,值可能是无穷大或 NaN,可能还有其他值。

如果问题是 d2 的值不足以让您的算法产生准确的结果,那么您必须避免float使用值,和/或使用数值更稳定的算法。

于 2010-04-07T10:59:02.293 回答
2

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

由于浮点数在内存中的表示方式,42.479999999999997 是 double 可以具有的最接近 42.48 的表示。

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

它解释了那里发生的事情。不幸的是,您对它的存储无能为力。

于 2010-04-07T09:08:47.577 回答