c++ 中的浮点数 a = -a 和 a *= -1 有区别吗?

Is there a difference between a = -a and a *= -1 for floats in c++?

对于 signed int、float 和 double 等类型?因为,如果我没记错的话(一个很大的如果),这是一个稍微翻转的问题,我想是否有一个明确的操作可以使代码 运行 更快?

编辑: 好吧,只是浮点型,int部分是我忘记动脑了,sorry)

-aa * -1 是不同的操作。1 C++ 标准中没有任何内容公开要求实现为它们产生不同的结果,因此编译器可以将它们视为相同的。 C++ 标准中的任何内容都不需要实现对它们进行相同的处理,因此编译器可能会以不同的方式处理它们。

C++ 标准在指定浮点运算方面非常宽松。 C++ 实现可以将一元 - 视为数学运算,因此 -a 等价于 0-a。另一方面,C++ 实现可以将一元 - 视为 IEEE-754 negate(x) 操作,每个IEEE 754-2008 5.5.1:

negate(x) copies a floating-point operand x to a destination in the same format, reversing the sign bit. negate(x) is not the same as subtraction(0, x)…

差异包括:

  • 取反是位级操作;它翻转符号位,不会发出 1 异常信号,即使操作数是信号 NaN,并且可能传播非规范编码(与十进制格式相关)。
  • 减法和乘法是数学运算;他们将发出 2 异常情况信号,并且不会传播非规范结果。

因此,您可能会发现编译器为 a = -a; 生成的 XOR 指令仅翻转符号位,但为 a *= -1; 生成乘法指令。只有当实现不支持浮点标志、陷阱、信号 NaN 或任何其他使否定和 subtraction/multiplication 可区分的东西时,它才应该为 a *= -1; 生成 XOR 指令。

Godbolt shows that x86-64 Clang 11.0.0 with default options uses xor for -a and mulss for a * -1. But x86-64 GCC 10.2 对两者都使用 xorps

脚注

1 我在这里隔离了 -* 操作;作业部分不感兴趣。

2 这里使用 IEEE-754 意义上的“信号”,表示出现异常情况的指示。这可能会导致出现一个标志,并且可能会导致影响程序控制的陷阱。它与C++信号不一样,虽然陷阱可能会导致C++信号。