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

For types like signed int and float and double etc? Because, if I remember correctly (a very big if), it's a matter of flipping a bit and I thought whether there's an explicit operation that would do it to make code run faster?

Edit: okay, just for floating-point types, the int parts were me forgetting to use my brain, sorry)

Upvotes: 4

Views: 169

Answers (1)

Eric Postpischil
Eric Postpischil

Reputation: 224102

-a and a * -1 are different operations.1 Nothing in the C++ standard overtly requires an implementation to produce different results for them, so a compiler may treat them as identical. Nothing in the C++ standard requires an implementation to treat them identically, so a compiler may treat them differently.

The C++ standard is largely lax about specifying floating-point operations. A C++ implementation could consider unary - to be a mathematical operation so that -a is equivalent to 0-a. On the other hand, a C++ implementation could consider unary - to be the IEEE-754 negate(x) operation, which per 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)…

Differences include:

  • Negation is a bit-level operation; it flips the sign bit, does not signal1 an exception even if the operand is a signaling NaN, and may propagate non-canonical encodings (relevant for decimal formats).
  • Subtraction and multiplication are mathematical operations; they will signal2 exceptional conditions and do not propagate non-canonical results.

Thus, you might find that a compiler generates an XOR instruction for a = -a; that merely flips the sign bit but generates a multiply instruction for a *= -1;. It should generate an XOR instruction for a *= -1; only if the implementation does not support floating-point flags, traps, signaling NaNs, or anything else that makes negation and subtraction/multiplication distinguishable.

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 uses xorps for both.

Footnotes

1 I have isolated the - and * operations here; the assignment portion is not of interest.

2 “Signal” is used here in the IEEE-754 sense, meaning an indication is given that an exceptional condition has occurred. This can result in a flag being raised, and it may cause a trap that affects program control. It is not the same as a C++ signal, although the trap may cause a C++ signal.

Upvotes: 4

Related Questions