9

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.

CodeSmile
  • 64,284
  • 20
  • 132
  • 217
  • I would imagine that `x*= -1` would actually need to perform the multiplication of the binary representation of `x`, so O(n) where n is the number of bits of the input. `x = -x` would probably simply swap the signed bit: O(1). In reality, would a compiler optimize it? Probably so. – erip May 27 '15 at 13:17
  • 4
    I would expect it to be completely compiler dependent. – Mike Wise May 27 '15 at 13:18
  • Compiler may generate same or different instructions for both cases. Functionally both are same after different language rules. So choose the one that is more readable for you. – Mohit Jain May 27 '15 at 13:18
  • 3
    Also, I don't think this is a duplicate. OP is asking about difference -- not about which is more efficient. Can someone with higher reputation unmark? – erip May 27 '15 at 13:19
  • I understand the implications of float math and rounding errors but I'm not that interested in equality, rather if there's some other reason to prefer multiplication. Ie cases where -f creates a fundamentally different value than *-1.0. – CodeSmile May 27 '15 at 13:28
  • I think that is always better to keep in mind the idea to not demand all to the compiler. If you know that an addition has a faster execution than a multiplication why do you have to use a multiplication? In this case a change of sign is for sure faster than a multiplication. Probably the compiler knows that, but ... :) – Sir Jo Black May 27 '15 at 13:33
  • @erip: Amusing point, which probably only applies to multiple-precision arithmetic libraries. After all, normal hardware will probably negate a standard-size integer in a single instruction. – PJTraill May 27 '15 at 14:25
  • 1
    One reason for the `*= -1` idiom could be that it also applies when the left hand side is complex and has side-effects: `(f ? a : b)[prime_after(i++)] *= -1` or a little more plausibly: `parity[state][getc()] *= -1` or even simply `variable_with_a_long_explanatory_name *= -1`. And once one has adopted an idiom, one tends to stick with it in other situations. – PJTraill May 27 '15 at 14:32
  • I don't think floating point rounding errors will apply in this case, the result will always be exact. I even did a test against infinity, it behaved as expected: http://ideone.com/ZZkXjF. A good optimizing compiler should realize that these are the same operation and generate the same code for both cases, so speed won't be an issue either. – Mark Ransom May 27 '15 at 15:35
  • @erip: amusing point, but only the original close voter can retract his vote. It seems that happened here, but really it depends on (1) the close-voter revisiting the question, and (2) the close-voter changing his opinion, based on a question edit or a discussion. – Jongware May 27 '15 at 17:23

2 Answers2

6

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.

Community
  • 1
  • 1
PJTraill
  • 1,353
  • 12
  • 30
2

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.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256