4

I've occasionally noticed some C code insisting on using 0 - x to get the additive complement of x, rather than writing -x. Now, I suppose these are not equivalent for types smaller in size than int (edit: Nope, apparently equivalent even then), but otherwise - is there some benefit to the former rather than the latter form?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 2
    Actually, being promoted to `int` happens in either case. The only case I see them having a different effect is if `x` is floating-point. – Deduplicator Aug 01 '21 at 21:15
  • *"Now, I suppose these are not equivalent for types smaller in size than int"* - They are actually [6.5.3.3p3](https://port70.net/~nsz/c/c11/n1570.html#6.5.3.3p3). Which makes this somewhat interesting. – StoryTeller - Unslander Monica Aug 01 '21 at 21:15
  • 3
    These days I would just guess readability. In K&R days maybe they want to avoid `y=-x` where `=-` was an alternative token for `-=` – M.M Aug 01 '21 at 21:16
  • 5
    As expected, there is a difference for floating-point zero: https://coliru.stacked-crooked.com/a/bc2ec461b152cefc – Deduplicator Aug 01 '21 at 21:22
  • Maybe because they want to be sure to get the integer promotion (not knowing that it happens in boths cases)? – Bob__ Aug 01 '21 at 21:22
  • 3
    It would be interesting to see where this _occasionally_ comes from. – Roland Illig Aug 01 '21 at 21:22
  • 1
    If i used it, it would be if it was a mathematical context where I want to make clear that a parameter is zero – klutt Aug 01 '21 at 21:41
  • 2
    Depending on toolchain, `unsigned int x; x = -x;` may result in a warning, while `unsigned int x; x = 0 - x;` does not. For floating-point types, unary minus is usually mapped to the IEEE-754 "negate" operation, which is a bit-level operation, not an arithmetic operation. So `-x` behaves different from `0 - x` for zero and NaN operands. – njuffa Aug 01 '21 at 22:39
  • @Deduplicator: That sounds like an answer. – einpoklum Aug 01 '21 at 22:44
  • @RolandIllig: I'm doing some work on a [standalone printf-and-friends implementation](https://github.com/eyalroz/printf/) (forked from [here](https://github.com/mpaland/printf), and it [exhibits this artifact](https://github.com/eyalroz/printf/blob/master/printf.c#L773). I remember seeing this before but I forget where. – einpoklum Aug 01 '21 at 22:48
  • I wonder if the author of that code thought that `0-x` would avoid the bug when `x == LLONG_MIN`. Unfortunately it does not, and there is UB in that case, whether `0-x` or `-x` is used. (Your version has the bug too, btw, and for other signed integer types as well.) – Nate Eldredge Aug 01 '21 at 23:26
  • 2
    This looks like a private joke... Here are more graphic alternatives: `0-x-0` and `x-x-x` (aka *the winning move*) – chqrlie Aug 02 '21 at 08:42
  • 1
    I would say it depends entirely on whether there's a comment on the line of code or not. *If* there's a comment, and if it says something like `/* We don't want an IEEE-754 -0 here */`, there's your answer. But if not, I'd assert there's no reason for it at all, and that the original programmer was likely either careless or confused. – Steve Summit Aug 02 '21 at 13:19
  • 1
    @SteveSummit: No comment :-P – einpoklum Aug 02 '21 at 14:29
  • @NateEldredge: Fixed, albeit in a somewhat brittle kind of way. – einpoklum Aug 02 '21 at 16:45

2 Answers2

4

tl;dr: 0-x is useful for scrubbing the sign of floating-point zero.

(As @Deduplicator points out in a comment:)

Many of us tend to forget that, in floating-point types, we have both a "positive zero" and a "negative zero" value - flipping the sign bit on and off leaves the same mantissa and exponent. Read more on this here.

Well, it turns out that the two expressions behave differently on positive-signed zero, and the same on negative-signed zero, as per the following:

value of x value of 0-x value of -x
-.0 0 0
0 0 -.0

See this on Coliru.

So, when x is of a floating-point type,

  • If you want to "forget the sign of zero", use 0-x.
  • If you want to "keep the sign of zero", use x.

For integer types it shouldn't matter.


On the other hand, as @NateEldredge points out the expressions should be equivalent on small integer types, due to integer promotion - -x translates into a promotion of x into an int, then applying the minus sign.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
3

There is no technical reason to do this today. At least not with integers. And at least not in a way that a sane (according to some arbitrary definition) coder would use. Sure, it could be the case that it causes a cast. I'm actually not 100% sure, but in that case I would use an explicit cast instead to clearly communicate the intention.

As M.M pointed out, there were reasons in the K&R time, when =- was equivalent to -=. This had the effect that x=-y was equivalent to x=x-y instead of x=0-y. This was undesirable effect, so the feature was removed.

Today, the reason would be readability. Especially if you're writing a mathematical formula and want to point out that a parameter is zero. One example would be the distance formula. The distance from (x,y) to origo is sqrt(pow(0-x, 2), pow(0-y, 2))

klutt
  • 30,332
  • 17
  • 55
  • 95
  • That's a good observation about the primordial form of the `-=` operator (and other op-equal operators). That may very well explain the source of the idiom the OP asks about. – John Bollinger Aug 01 '21 at 23:05