在c++中是浮点加法可交换的
Is floating point addition commutative in C++?
对于浮点数,是否保证a + b
与1 b + a
相同?
我相信IEEE754保证了这一点,但是c++标准并没有指定必须使用IEEE754。唯一相关的文本似乎来自[expr.add]#3:
二元+运算符的结果是操作数的和。
数学运算"sum"是可交换的。然而,数学运算"answers";也是关联的,而浮点加法肯定是不是关联的。所以,在我看来我们不能得出和的交换性在数学中的意思是这个引号指定c++中的交换性。
脚注1:
"Same"就像位相同,像memcmp
而不是==
,以区分+0和-0。IEEE754将+0.0 == -0.0
视为真值,但对于带符号的零也有特定规则。+0 + -0
和-0 + +0
在IEEE754中都产生+0
,对于等量的对号值的加法也是如此。遵循IEEE语义的==
将隐藏符号零的非交换性,如果这是标准的话。
同样,如果输入是NaN, a+b == b+a
在IEEE754数学中为假。memcmp
将说明两个NaN是否具有相同的位模式(包括有效载荷),尽管我们可以将NaN传播规则与有效数学运算的交换性分开考虑。
甚至不需要a + b == a + b
。其中一个子表达式可能以更高的精度保存加法运算的结果,例如,当使用多个加法运算时,需要将其中一个子表达式临时存储在内存中,而另一个子表达式可以保存在寄存器中(具有更高的精度)。
如果不保证a + b == a + b
,则不能保证a + b == b + a
。如果a + b
不必每次都返回相同的值,并且值不同,那么其中一个必然不会等于b + a
的一个特定求值
不,c++语言一般不会对硬件做出这样的要求。只定义了操作符的结合性。
各种疯狂的事情发生在浮点运算中。也许,在某些机器上,对一个不正常的数字加零会产生零。可以想象,在向内存中的异常寄存器中添加零值寄存器的情况下,机器可以避免更新内存。一个愚蠢的编译器可能总是把LHS放在内存中,而把RHS放在寄存器中。
请注意,如果你想要控制你得到的操作,那么使用非交换加法的机器需要特别定义表达式如何映射到指令。左边的是第一个机器操作数还是第二个?
这样的ABI规范,同时提到表达式和指令的构造,将是相当病态的。
c++标准非常明确不保证IEEE 754。该库确实支持IEC 559(基本上只是IEC版本的IEEE 754标准),因此您可以检查底层实现是否使用IEEE 754/IEC 559(当它使用时,您可以依赖于它保证的内容,当然)。
在很大程度上,C和c++标准假定无论底层硬件如何工作,这些基本操作都将被实现。对于像IEEE 754这样常见的东西,他们会让你检测它是否存在,但仍然不需要它。
对于使用IEEE FP数学的c++实现,除NaN有效负载外,任何给定精度的加法都是可交换的。
Marc Glisse评论:
对于内置类型,gcc将交换+的操作数而不需要任何特别的注意事项。
-
有限输入非零结果是简单情况,明显可交换。加法是"基本"的加法之一。FP数学运算,因此IEEE754要求结果"正确舍入"。(舍入误差<= 0.5 ulp),因此只有一个可能的数值结果,并且只有一个位模式表示它。
非ieee FP数学可能允许更大的舍入误差(例如允许尾数的LSB中的off-by- 1,因此舍入误差<= 1 ulp)。它可以与最终结果不可交换,这取决于哪个操作数是哪个。我想大多数人会认为这是一个糟糕的设计,但c++可能不会禁止它。
-
如果结果为零(具有相同大小但符号相反的有限输入),则在IEEE数学中始终为
+0.0
。(或-0.0
在roundTowardNegative舍入模式)。该规则适用于+0 + (-0.0)
和+0.0
的情况。参见 IEEE浮点标准中的(+0)+(-0)是什么?不同幅度的输入不能下流到零,除非FPU在flush-to-zero模式下工作时具有亚正常输入(亚正常输出向零四舍五入)。在这种情况下,你可以得到
-0.0
作为结果,如果确切的结果是负的。但它仍然是可交换的 -
加法可以从
-0 + -0
产生-0.0,这是微不足道的交换,因为两个输入是相同的值。 -
-Inf +任何有限的都是-Inf。+∞+任何有限的都是+∞。+Inf + -Inf = NaN。这两个都不依赖于顺序。
-
NaN +任何东西或任何东西+ NaN是NaN。"payload"(尾数)取决于FPU。IIRC,保留之前NaN的有效载荷。
-
NaN + NaN生成NaN。如果我没记错的话,没有规定保留哪个NaN有效负载,或者是否可以发明一个新的有效负载。几乎没有人对NaN有效载荷做任何事情来跟踪它们的来源,所以这不是什么大问题。
c++中+
的两个输入都将提升为匹配类型。如果两种输入类型不匹配,则具体到两种输入类型的宽度。所以没有不对称的类型
对于a+b == b+a
本身,由于IEEE ==
语义(不是因为+
语义),对于nan可能为假,与a+b == a+b
相同。
使用严格的FP数学(C语句之间没有额外的精度,例如gcc -ffloat-store
,如果在x86上使用传统的x87数学),我认为相等性相当于!isunordered(a,b)
,它测试它们中的任何一个是否为NaN。
否则,可能编译器可以使用其中一个较早的代码而不使用另一个,并使用a
和b
的更高精度值对其中一个进行计算。(严格的ISO c++要求,即使FLT_EVAL_METHOD==2(如x87),高精度临时值也只能存在于表达式中,而不是跨语句,但默认情况下gcc
不尊重这一点。只能使用g++ -std=c++03
或其他代替gnu++20
的东西,或者专门为x87使用-ffloat-store
)
在使用FLT_EVAL_METHOD == 0
的c++实现上(表达式内的临时变量没有额外的精度),这种优化差异的来源将不会是一个因素。
- C++将浮点指针值舍入为小数位数
- 如何防止 c++ 在从浮点型转换为双精度型(不适用于 IO)时添加额外的小数?
- 为什么在浮点中从大到小会引入更多的误差
- 我可以信任表示整数的浮点或双精度来保持精度吗
- 为什么mpfr_printf与十六进制浮点(%a转换说明符)的printf不同
- C++Lua用户数据扰乱了Mathfu浮点值
- 将CHW格式的浮点向量转换为cv::Mat
- 在 c++ 中,能够将浮点类型加 1 的最大整数是多少?
- 在浮点精度成为一个问题之前,可以将多少个浮点值加在一起
- 处理融合乘加浮点不准确性的通用方法
- C++ std::bad_alloc 加载 190 万行浮点值文件 - 提供源代码
- 可测试的无效浮点值.(浮点值为NULL)
- 在OpenCV中保存并加载浮点值
- 从新发行版和不同发行版加载LD_PRELOAD libstdc++.so.6和libc.so.6时出现浮点异常
- 浮点运算是如何在一个大数字上加一的
- 如何在C++中将精确的浮点值保存并恢复到可读文件中
- 加载常量的FLD浮点指令
- 在c++中是浮点加法可交换的
- 在文件中存储32位浮点值的可移植性问题
- 32位,64位和80位浮点IEEE-754的可表示值范围