根据IEEE浮点数标准,(+0)+(-0)等于什么?

13

我是否正确,任何浮点数的算术运算都遵循IEEE浮点数标准且定义明确?如果是,那么仅出于好奇,(+0)+(-0)等于多少呢?在C++或其他常用编程语言中是否有一种方法可以验证这些内容?


我的直觉是负零,但我无法证明。你可以通过将1除以该值来轻松检查是否为正零或负零。+0 => 正无穷等等。 - Wai Ha Lee
在C++中?我会有点担心尝试那样做;一个优化编译器会看到这样的常量表达式并消除它。你需要玩弄编译器标志来消除这种情况。 - Andon M. Coleman
1
可能是重复问题:https://dev59.com/o4Dba4cB1Zd3GeqPHKtp。(是的,它被标记为Java,但它是IEEE 754的属性,而不是特定于Java。) - Mark Dickinson
3
请注意:即使IEEE754已经明确定义了一个操作,这并不意味着在C++中进行double类型的计算是明确定义的。例如,根据寄存器的可用性等因素,计算可能会以高于double的精度内部完成,也可能不会。 - M.M
4个回答

17

IEEE 754浮点数规范指出,对于带符号的零值而言,+0.0 + -0.0 的结果取决于舍入模式。在默认舍入模式下,它将为 +0.0。当向负无穷舍入时,它将为 -0.0

您可以在C++中进行如下检查:

#include <iostream>

int main() {
    std::cout << "+0.0 + +0.0 == " << +0.0 + +0.0 << std::endl;
    std::cout << "+0.0 + -0.0 == " << +0.0 + -0.0 << std::endl;
    std::cout << "-0.0 + +0.0 == " << -0.0 + +0.0 << std::endl;
    std::cout << "-0.0 + -0.0 == " << -0.0 + -0.0 << std::endl;
    return 0;
}

输出

+0.0 + +0.0 == 0
+0.0 + -0.0 == 0
-0.0 + +0.0 == 0
-0.0 + -0.0 == -0

出于兴趣,如何更改舍入模式? - Wai Ha Lee
在 C 语言中,有一个名为 fesetround() 的函数,它位于 fenv.h 头文件中。我不确定这个函数是否也适用于 C++。 - njuffa
3
这样。不幸的是,大多数编译器在优化常量表达式时,如+0.0 + -0.0,会忽略舍入模式(参见GCC bug 34678)。 - Tavian Barnes
1
是的,在C++中可以通过<cfenv>使用fesetround函数(尽管在C语言中忽略它的编译器也可能会在C++中忽略它)。 - M.M

3

我的回答涉及IEEE 754:2008,它是目前的标准版本。

在IEEE 754:2008标准中:

第4.3节涉及在执行算术操作时对值进行舍入以适合尾数的位。

4.3 舍入方向属性

舍入将被视为无限精确的数字,并在必要时修改它以适合目标格式,同时在适当时发出不准确异常、下溢或上溢信号(见7)。除非另有说明,否则每个操作都应该按照首先生成一个无限精度和无限范围的中间结果,然后根据本条款中的一个属性舍入该结果来执行。

舍入方向属性影响所有可能不准确的计算操作。不准确的浮点数数值结果总是与未舍入的结果具有相同的符号。

舍入方向属性影响精确零和(见6.3)之和的符号,也影响超过其门限值时会发出上溢和下溢信号。


第6.3节规定了在处理特殊值(NaN、无穷大、+0、-0)的算术运算时符号位的取值。

6.3 符号位

当两个有相反符号的操作数之和(或两个同符号的操作数之差)等于零时,在除 roundTowardNegative 以外的所有舍入方向属性下,该和(或差)的符号应为 +0;在这种属性下,一个精确零和(或差)的符号应为 -0。

然而,x + x = x − (−x) 在 x 为零时仍保持与 x 相同的符号。

(强调是我加的)

换句话说,(+0) + (-0) = +0,除非 舍入模式roundTowardNegative,在这种情况下,它为 (+0) + (-0) = -0。




在C#的上下文中:

根据§7.7.4C#语言规范(强调我的):

  • 浮点数加法:

float operator +(float x, float y);

double operator +(double x, double y);

和按照IEEE 754算术规则计算。以下表格列出了所有可能的非零有限值、零、无穷大和NaN的结果组合。在表格中,x和y是非零有限值,z是x+y的结果。如果x和y具有相同的大小但符号相反,则z为正零。如果x+y太大而无法表示为目标类型,则z是与x+y相同符号的无穷大。

 +  •  x      +0     -0     +∞     -∞    NaN
•••••••••••••••••••••••••••••••••••••••••••••
y   •  z      y      y      +∞     -∞    NaN
+0  •  x      +0     +0     +∞     -∞    NaN
-0  •  x      +0     -0     +∞     -∞    NaN
+∞  •  +∞     +∞     +∞     +∞     NaN   NaN
-∞  •  -∞     -∞     -∞     NaN    -∞    NaN
NaN •  NaN    NaN    NaN    NaN    NaN   NaN

(+0) + (-0) 在 C# 中的结果:

换句话说,根据规范,两个零的加法只有在是负零时才会得到负零。因此,对于原始问题的答案是:

按照 IEEE 浮点标准,(+0)+(-0) 等于什么?

答案是 +0。


C#中的四舍五入模式:

如果有人对于如何在C#中更改四舍五入模式感兴趣,Hans Passant 在 "Is there an C# equivalent of c++ fesetround() function?" 中指出:

不要在C#中调整FPU控制字。这是您可以想象到的最糟糕的全局变量。由于全局变量带来的标准痛苦,您进行的更改可能无法维持并会随意消失。当CLR处理异常时,内部异常处理代码将其重置。


1
假定标准舍入模式(如果您不知道舍入模式及其如何更改,则正在使用它)。
如果精确结果非零但太小而被舍入为零,则结果为+0,如果精确结果大于0,则为-0。仅在乘法和除法中出现此情况,加法和减法不会有此情况。
有几种情况下,精确结果为零。在这种情况下,在以下情况下结果为-0:(-0)+(-0)相加。减去(-0)-(+0)。其中一个因子为零,另一个因子具有相反的符号(包括(+0)*(-0))的乘法。将零除以另一个非零数,包括相反符号的无穷大。在所有其他情况下,结果为+0。
这个规则的一个不幸的副作用是,x + 0.0并不总是等同于x(当x为-0时不等)。另一方面,x - 0.0始终与x相同。此外,x * 0.0可能是+0或-0,具体取决于x。这阻止了一些支持IEE754精确计算的编译器进行优化,或使它们更加困难。

0

根据IEEE浮点标准,答案是+0。


1
这取决于舍入模式。对于roundTowardNegative而言,根据IEEE 754的第6.3节,结果应为-0.0 - Mark Dickinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接