CodeSmile
CodeSmile

Reputation: 64477

Is there a functional difference between "x = -x" and "x *= -1" when negating values?

Code example:

float f = 123.456;
int i = 12345;
// negate by changing sign
i = -i;
f = -f;
// negate by multiplying with -1
i *= -1;
f *= -1.0f;

Apart from aesthetics, is there any factual reason why one should prefer one way over the other?

To me multiplication seems a little unnecessary. But I see the multiplication style more often, and wanted to hear if there's a good reason for it, besides preference.

Upvotes: 9

Views: 389

Answers (2)

chux
chux

Reputation: 154218

Recommend to use what explains the code best, unless one is working with an old platform or compiler. In that case, use negation.

Following are some obscure differences:

int format with non-2's complement:
In days of yore, a multiplication by of 0 and -1 could result in 0 yet a negation of 0 could result in -0. Of course 0 and -0 have the same value, yet different signs. Not all non-2's complement machines worked the same concerning this.

Floating Point:
Speed: A smart compiler will create efficient and likely the same code for f *= -1.0f and f *= -f. Yet a lesser compiler may not identify this equivalence and perform one slower than the other.

Rounding: A negation need not invoke any rounding, yet a multiplication, even by 1.0, can involve a round. This happens in 2 cases: when the variables are of a higher precision (allowed in modern C, FLT_EVAL_METHOD) and in machines (more like relics) that performed their FP using base-16 rather than base-2. In this latter case, the precision wobbles (e.g IBM Floating Point) and a simple multiplication would present a rounded product. This behavior is uncommon these days.

There also exist FP formats that have multiple representations for the same value, but a multiplication by 1 would return the preferred format, whereas negation would simply flip the sign bit. The results are the same value, but different bit patterns of the FP number.

Upvotes: 2

PJTraill
PJTraill

Reputation: 1420

Based on my answer to “fastest-way-to-negate-a-number

Speed

The answers to a similar question have indicated that:

  • You should forget about speed and choose the idiom that you find most readable.
  • Almost all compilers generate equivalent optimal code (probably a single instruction) for anything like a = -a, a *= -1 etc.
    • But though MSVS 2012 (optimised?) uses one instruction for each, they take 1 cycle for = -a and 3 for *= -1.
  • Any attempt to make it faster will make it far less readable and could easily make it slower.
  • If you need to optimise, you should start by analysing generated code and performance.

Side-effects and irredundancy

There is however a practical advantage to the *= -1 idiom: you only have to write the left hand side once, and it is only evaluated once. This is relevant when the LHS is long, complex or expensive or may have side-effects:

(valid ? a : b)[prime_after(i++)] *= -1;
*look_up (input) *= -1;  // Where look_up may have side-effects
parity[state][(unsigned int)getc(stdin)] *= -1;
variable_with_a_long_explanatory_name *= -1;

A further advantage of this irredundancy is that when seeking a bug one can be certain that the number really is being negated in situ, and that there is no subtle difference in the two expressions.

Once one has adopted an idiom, one tends to stick with it in other situations, so it is understandable to stick to *= -1.

Upvotes: 6

Related Questions