【问题标题】:Why two float type variables have different values [duplicate]为什么两个浮点类型变量具有不同的值[重复]
【发布时间】:2013-09-10 18:45:46
【问题描述】:

我有两个大小接近 1000 的整数向量,我要做的是检查这两个向量的平方整数之和是否相同。所以我写了以下代码:

std::vector<int> array1;
std::vector<int> array2;
... // initialize array1 and array2, and in the experiment all elements
    // in the two vectors are the same but the sequence of elements may be different.
    // For example: array1={1001, 2002, 3003, ....} 
   //               array2={2002, 3003, 1001, ....}
assert(array1.size() == array2.size());
float sum_array1 = 0;
float sum_array2 = 0;
for(int i=0; i<array1.size(); i++)
       sum_array1 +=array1[i]*array1[i];
for(int i=0; i<array2.size(); i++)
       sum_array2 +=array2[i]*array2[i];

我希望sum_array1 应该等于sum_array2,但实际上在我的应用程序中我发现它们是不同的sum_array1 = 1.2868639e+009sum_array2 = 1.2868655e+009。我接下来所做的是将sum_array1sum_array2 的类型更改为double 类型,如下代码所示:

 double sum_array1 = 0;
    double sum_array2 = 0;
    for(int i=0; i<array1.size(); i++)
           sum_array1 +=array1[i]*array1[i];
    for(int i=0; i<array2.size(); i++)
           sum_array2 +=array2[i]*array2[i];

这次sum_array1 等于sum_array2 sum_array1=sum_array2=1286862225.0000000。我的问题是为什么它会发生。谢谢。

【问题讨论】:

  • 您没有提供足够的信息。这两个计算是相同的,因此它们应该产生相同的结果。问题必须在其他地方,在您未显示的代码中。
  • 永远不要比较浮点数是否相等!要么使用任意或固定精度的整数类,要么在可接受的误差范围内比较浮点数。
  • 这些向量的长度是否相同?您可以对它们进行排序并按元素进行比较吗?你能利用恒等式 sq(a) - sq(b) == (a + b) * (b - a) 吗?只是一些需要考虑的事情。
  • 我认为这个问题不应该结束。问题不仅仅是比较浮点数。这是关于为什么以不同的顺序添加浮点数会产生不同的结果。如果 SO 上有该问题的重复项,我将投票标记为重复项,但这不是“比较浮点数”常见问题的重复项。

标签: c++


【解决方案1】:

在两个循环中,您添加的数字相同,但顺序不同。一旦总和超过float 可以精确表示的整数值,您就会开始失去精度,并且总和最终可能会略有不同。

一个供你尝试的实验:

float n = 0;
while (n != n + 1)
    n = n + 1;
//Will this terminate? If so, what is n now?

如果你运行它,你会发现循环实际上终止了 - 这似乎完全违反直觉,但根据 IEEE single-precision floating point 算法的定义是正确的行为。

您可以尝试相同的实验,将float 替换为double。您会看到同样的奇怪行为,但这次循环将在 n 大得多时终止,因为 IEEE double-precision floating point 数字可以实现更高的精度。

【讨论】:

  • 如果您使用(不经意间)激进的优化,循环可能永远不会终止,无论n 是什么,它都会将n!=n+1 呈现为false,即为此推理使用数学上精确的算术(不包括@987654331 @ = 无穷大)。
  • @Walter 这个答案的重点。
【解决方案2】:

浮点表示法(通常为 IEEE754)使用有限位来表示小数,因此 operations with floating-point numbers result in precision loss

通常,与常识相反,如果 a 是浮点变量,则像 a == ((a+1)-1) 这样的比较会导致 false

解决办法:

要比较两个浮点数,您必须使用一种“精度损失范围”。也就是说,如果一个数字不同于小于 precision-loss-range 的其他数字,则您认为该数字相等:

//Supposing we can overload operator== for floats
bool operator==( float lhs , float rhs)
{
    float epsilon = std::numeric_limits<float>.epsilon();

    return std::abs(lhs-rhs) < epsilon;
}

【讨论】:

  • 但是这两个计算相同。浮点数不是随机的,结果应该是一样的。
  • 编辑不正确。有些值不存在这种相等性,但作为一般陈述,它为真。
  • @PeteBecker,如果它们以不同的顺序添加,它们就不一样了。
  • @PeteBecker 取决于将哪些浮点数放入 CPU 寄存器以及何时放入。 CPU 寄存器可能比浮点型大。
  • @joshpoley - 你是对的;我没注意到。
【解决方案3】:

浮点值的大小是有限的,因此只能以有限的精度表示实数值。当您需要比它们存储的精度更高的精度时,这会导致舍入错误。

特别是,当将一个小数(例如您要求和的数)添加到一个大得多的数(例如您的累加器)时,与小数相比,精度损失可能会很大,从而产生很大的误差;并且错误会根据顺序而有所不同。

通常,float 具有 24 位精度,对应于大约 7 个小数位。您的累加器需要 10 位小数(大约 30 位),因此您会遇到这种精度损失。通常,double 有 53 位(大约 16 位小数),因此您的结果可以准确表示。

64 位整数可能是这里的最佳选择,因为所有输入都是整数。使用整数可以避免精度损失,但如果输入太多或太大,则会带来溢出的危险。

如果您不能使用足够宽的累加器,为了最大限度地减少错误,您可以对输入进行排序,以便首先累加最小值;或者你可以使用更复杂的方法,例如Kahan summation

【讨论】:

    【解决方案4】:

    double 具有更多位,因此比float 拥有更多信息。当您向浮点数添加值时,它最终会在 sum_array1 和 sum_array2 的不同时间对信息进行四舍五入。

    根据输入值,使用双精度浮点数时可能会遇到同样的问题(如果值足够大)。

    通过网络搜索“您需要了解的有关浮点数的所有信息”,您可以很好地了解这些限制以及如何最好地处理这些限制。

    【讨论】:

    • @Pete:计算结果不相同。它们的顺序不同,这会影响结果。如果您循环访问array1 两次,它们相同
    猜你喜欢
    • 2018-07-05
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-31
    • 1970-01-01
    相关资源
    最近更新 更多