关于c ++:负零(-0.0)与正零(+0.0)相比的行为

Behaviour of negative zero (-0.0) in comparison with positive zero (+0.0)

在我的代码中

1
float f = -0.0; // Negative

并与负零比较

1
f == -0.0f

结果将为true

1
float f = 0.0; // Positive

并与负零比较

1
f == -0.0f

同样,结果将是true而不是false

为什么在两种情况下结果都是正确的?

这是一个测试它的MCVE(在coliru上直播):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

int main()
{
    float f = -0.0;

    std::cout<<"==== >" << f <<std::endl<<std::endl;

    if(f == -0.0f)
    {
        std::cout<<"true"<<std::endl;
    }
    else
    {
        std::cout<<"false"<<std::endl;
    }
}

输出:

1
2
3
==== > -0  // Here print negative zero

true


C ++中的浮点算法通常是IEEE-754。此规范与实数集的数学定义不同。

该规范为值零定义了两种不同的表示形式:正零和负零。还定义了这两个表示必须比较等于,因此根据定义:

1
+0.0 == -0.0

关于其原因,David Goldberg 1991-03(链接到IEEE网站的IEEE-754页)在其论文《每个计算机科学家应该了解的浮点运算法则》中写道:

In IEEE arithmetic, it is natural to define log 0 = -∞ and log x to be a NaN when x < 0. Suppose that x represents a small negative number that has underflowed to zero. Thanks to signed zero, x will be negative, so log can return a NaN. However, if there were no signed zero, the log function could not distinguish an underflowed negative number from 0, and would therefore have to return -∞.


这是因为带符号的负零必须将true与零进行比较:即-0.0 == 0.0-0f == 0f-0l == 0l

这是C ++编译器支持的任何浮点方案的要求。

(请注意,当今大多数平台都使用IEEE754浮点,并且该行为已在该规范中明确记录。)


如果实现支持带符号的零(例如,由于使用IEEE浮点),则C ++ 11引入了诸如std::signbit()的功能,该功能可以检测带符号的零,而std::copysign()的功能可以在浮点值之间复制符号位。除了那种事情,我不知道C ++标准中的任何引用,甚至提到带符号的零,更不用说比较它们的结果了。

C ++标准也没有规定任何浮点表示形式-这是实现定义的。

尽管不是确定的,但这些观察结果表明对有符号零的支持或对其进行比较的结果将由实现(即编译器)支持的浮点表示形式

IEEE-754是现代实现(即编译器)使用的最常见(尽管不是唯一)浮点表示形式。 IEEE-758"浮动点算法的IEEE标准"第5.11节第二段的当前版本(于2008年发布)说(粗体强调我的)

Four mutually exclusive relations are possible: less than, equal, greater than, and unordered. The last case arises when at least one operand is NaN. Every NaN shall compare unordered with everything, including itself. Comparisons shall ignore the sign of zero (so +0 = ?0). Infinite operands of the same sign shall compare equal.


因为0.0f和-0.0f相同,零的负数就是零