【问题标题】:How does DBL_MAX addition work?DBL_MAX 加法是如何工作的?
【发布时间】:2016-08-08 09:47:41
【问题描述】:

代码

#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

我无法理解为什么这两个函数的工作方式不同。这里发生了什么?

【问题讨论】:

  • 这两个函数都会导致未定义的行为。
  • @2501 但为什么是未定义的行为?
  • 签名变量溢出是UB。
  • 您的添加超出了数字限制:您所做的是未定义的行为。 double 和 long 的二进制编码是不同的,因此在这里有不同的结果可以被认为是“正常的”,但同样你的代码是未定义的行为,应该被认为是不可预测的
  • @utkarsh13 如果您将最大可能的数字和最大可能的数字相加,您希望结果是什么?

标签: c++ c double


【解决方案1】:

在 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),因此您不能依赖有符号整数溢出来执行我上面解释的操作。但是,在这种情况下,它似乎“很好”。


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

对于那些怀疑浮点情况是潜在 UB 的人:N4140 5/4 [expr] 说

如果在计算表达式期间,结果未在数学上定义或不在 其类型的可表示值,行为未定义。

就是这样。允许使用 inf 和 NaN 的东西,但在 C++ 和 C 浮点数学中不需要。仅当std::numeric_limits::is_iec559&lt;T&gt; 对所讨论的浮点类型为真时才需要。 (或者在 C 中,如果它定义了__STDC_IEC_559__ 。否则,附件 F 的内容不需要适用。)如果任何一个 iec 指标保证我们符合 IEEE 语义,那么行为就可以很好地完成我上面描述的操作。

【讨论】:

  • 浮点运算肯定不是未定义的行为吗?处理无穷大和 NaN 值在浮点类型中有很好的定义,不是吗?
  • @pmdj It is,例如允许-ffast-mathINFNaN 来自 IEEE 浮点,而不是允许但不需要它们的标准 C++。
  • @uhohsomebodyneedsaupper “定义STDC_IEC_559的实现应符合本附件中的规范。”在F1中。如果不是,则无需遵循 IEEE。 (我希望 n1256 是一个合理的文档,只是通过谷歌搜索发现的。)
  • @EOF "如果double 可以代表无穷大" 如果我们没有__STDC_IEC_559__,它可能会也可能不会。
  • @nwellnhof 这些引号在这里不适用,因为运算符+ 不是&lt;math.h&gt; 中的函数。另请注意,HUGE_VAL 等(在&lt;math.h&gt; 函数中溢出时返回)仅在定义了__STDC_IEC_559__ 时才需要扩展到某个无穷大。一般来说,它们扩展了一些正常数表达式。 (参见 N1256 中的 7.12/2 并再次注意 Anex F 仅在定义了上述宏时才适用。)
猜你喜欢
  • 1970-01-01
  • 2017-06-29
  • 2013-03-11
  • 2019-05-20
  • 2013-12-04
  • 2012-04-21
  • 2016-07-26
  • 2012-09-01
  • 1970-01-01
相关资源
最近更新 更多