#include<stdio.h>
#include<limits.h>
#include<float.h>

int f( double x, double y, double z){
  return  (x+y)+z == x+(y+z);
}

int ff( long long x, long long y, long long z){
  return  (x+y)+z == x+(y+z);
}

int main()
{
    printf("%d\n",f(DBL_MAX,DBL_MAX,-DBL_MAX));
    printf("%d\n",ff(LLONG_MAX,LLONG_MAX,-LLONG_MAX));
    return 0;
}

输出量
0
1

我无法理解为什么两个功能的工作方式不同。这是怎么回事

最佳答案

在C++和C标准看来,整数版本和浮点版本肯定会调用未定义行为,因为计算x + y的结果无法以执行该算术的类型表示。†因此,两个函数都可能产生或甚至做任何事情。

但是,许多现实世界的平台都为浮点运算提供了额外的保证,并以某种方式实现整数,从而使我们能够解释所获得的结果。

考虑到f,我们注意到许多流行的平台都实现了IEEE 754中描述的浮点数学运算。遵循该标准的规则,我们得到了LHS:

DBL_MAX + DBL_MAX = INF


INF - DBL_MAX = INF.

RHS yield
DBL_MAX - DBL_MAX = 0


DBL_MAX + 0 = DBL_MAX

因此LHS!= RHS。

转到ff:许多平台以二进制补码形式执行有符号整数计算。二进制补码的加法是关联的,因此只要优化程序不将其更改为与二进制补码规则相抵触的内容,比较就将为真。

后者是完全可能的(例如,参见this discussion),因此您不能依靠有符号整数溢出来完成我在上文中所做的解释。但是,在这种情况下,它似乎“不错”。

†请注意,这永远不适用于无符号整数算术运算。在C++中,无符号整数实现了2^NumBits模的算术运算,其中NumBits是该类型的位数。在这种算法中,每个整数都可以通过在[0, 2^NumBits - 1]中选择其等效类的代表来表示。因此,这种算法永远不会溢出。

对于那些怀疑浮点数是否可能是UB的人:N4140 5/4 [expr]说



就是这种情况。 inf和NaN内容是允许的,但在C++和C浮点数学中不是必需的。仅当有关浮点类型的std::numeric_limits::is_iec559<T>为true时才需要。 (或者在C中,如果它定义了__STDC_IEC_559__,则不需要应用附件F的内容。)如果任何一个iec指标都保证了我们的IEEE语义,那么该行为已得到很好的定义,可以完成我在上面描述的操作。

10-08 02:11