在C++中,针对浮点数,a=-a 和 a*= -1 之间是否有区别?

4

对于像有符号整数、浮点数和双精度等类型,是否有一种明确的操作可以快速实现翻转一个比特位?因为如果我记得正确(这是一个非常大的如果),这只是一件轻而易举的事情,我想知道是否有一个显式的操作可以使代码运行更快?

编辑:好的,仅针对浮点类型,我忘记使用我的脑子了,请原谅。


4
问问自己,是什么阻止编译器计算出只需翻转一位并生成执行此操作的机器代码?认为编译器必须 严格地 按照代码所说做事是初学者常见的误解。 - john
我在想是否有一个明确的操作 - 或许编译器足够聪明可以做到这一点?生成你的编译器所产生的汇编代码并查看一下。 - PaulMcKenzie
1
顺便提一下,一些编译器对负零的特殊处理意味着这个操作并不像翻转一个比特那么简单。 - john
@PaulMcKenzie:仅仅检查一个编译器在某种情况下的行为,并不能展示C++标准在所有情况下的规则。 - Eric Postpischil
1个回答

4

-aa * -1是不同的操作。1 C++标准中没有明确要求实现在这两者之间产生不同的结果,因此编译器可以将它们视为相同的。C++标准也没有要求实现将它们视为相同的,所以编译器可以将它们视为不同的。

C++标准在指定浮点运算方面非常宽松。C++实现可能认为一元的-是数学运算,使得-a等同于0-a。另一方面,C++实现也可能将一元的-视为IEEE-754 negate(x)操作,根据IEEE 754-2008 5.5.1:

negate(x)将一个浮点操作数x复制到一个目标中,该目标与源具有相同的格式,在复制时翻转符号位。 negate(x)不同于subtraction(0, x)…

它们的区别包括:

  • 取反是一个位级操作;它翻转了符号位,即使操作数是信号NaN,也不会引发异常,并且可能传播非规范编码(对于十进制格式是相关的)。
  • 减法和乘法都是数学运算;它们会发出异常条件并且不会传播非规范结果。

因此,你可能会发现编译器会生成一个XOR指令来执行a = -a;,只是翻转了符号位,但是会为a *= -1;生成一个乘法指令。如果实现不支持浮点标志、陷阱、信号NaN或其他使取反和减法/乘法可区分的内容,它应该只为a *= -1;生成一个XOR指令。

Godbolt显示x86-64 Clang 11.0.0使用默认选项为-a使用xor,为a * -1使用mulss。但是x86-64 GCC 10.2为两者都使用xorps

脚注

1 我在这里仅隔离了-*操作;赋值部分不是我们关注的内容。

2 “Signal” 在这里是按照 IEEE-754 规范使用的术语,表示发生了异常情况并给出了相应的指示。这可能会导致标志被触发,还可能会引起影响程序控制的陷阱。它不同于 C++ 信号,尽管陷阱可能会导致 C++ 信号。


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