代码
#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
我无法理解为什么两个函数的工作方式不同。这里发生了什么?
答案 0 :(得分:4)
在C ++和C标准的眼中,整数版本肯定和浮点版本可能会调用未定义的行为,因为计算结果x + y
在执行算术的类型中是不可表示的。 †所以这两个函数都可能产生甚至做任何事情。
然而,许多真实世界的平台为浮点运算提供了额外的保证,并以某种方式实现了整数,让我们可以解释你得到的结果。
考虑到f
,我们注意到许多流行的平台实现了IEEE 754中描述的浮点数学。遵循该标准的规则,我们得到了LHS:
DBL_MAX + DBL_MAX = INF
和
INF - DBL_MAX = INF.
RHS收益率
DBL_MAX - DBL_MAX = 0
和
DBL_MAX + 0 = DBL_MAX
因此LHS!= RHS。
继续ff
:许多平台以二进制补码执行有符号整数计算。二进制补码的加法是偶像的,所以只要优化器不会将它改为与二进制补码规则相矛盾的东西,比较就会产生真实。
后者是完全可能的(例如参见this discussion),所以你不能依赖有符号整数溢出来做我上面解释的。然而,似乎它很好&#34;在这种情况下。
†请注意,这绝不适用于无符号整数运算。在C ++中,无符号整数实现算术模2^NumBits
,其中NumBits
是该类型的位数。在此算法中,可以通过在[0, 2^NumBits - 1]
中选择其等价类的代表来表示每个整数。所以这个算术永远不会溢出。
对于那些怀疑浮点情况是潜在的UB的人:N4140 5/4 [expr]说
如果在评估表达式期间,结果未在数学上定义或不在范围内 其类型的可表示值,行为未定义。
情况就是这样。允许使用inf和NaN,但在C ++和C浮点数学中不需要。仅当std::numeric_limits::is_iec559<T>
对于所讨论的浮点类型为真时才需要它。 (或者在C中,如果它定义__STDC_IEC_559__
。否则,附件F的东西不需要应用。)如果任何一个iec指示符保证我们的IEEE语义,那么行为很好地定义为执行我上面描述的。