News for hackers. Stuff that matters.
Re:Subtraction is not comparison by ufo on Thursday December 25, 2014 @10:04PM (#8797868)
Subtraction is comparison at the assembly level though. C sadly doesn't give any way to interact with the overflow flags though...
Re:Subtraction is not comparison by userbinator on Friday December 26, 2014 @08:53AM (#8798822)
Because that overflag makes all the difference (no pun intended), I wouldn't say that they are the same, but that comparison usually involves subtraction.

I remember coming across a set of lecture slides for a CS course on architecture that used its own toy architecture with neither flags nor separate comparison instructions - saying that comparison is the same as subtraction gives people the entirely wrong idea, and leads to the sort of bugs mentioned in this article.

Re:Subtraction is not comparison by vardump on Thursday December 25, 2014 @10:39PM (#8797938)
Yup. On pretty much any architecture I can think of, a CMP is exactly like a SUB that does not store result, but sets only flags.
Re:Subtraction is not comparison by haberman on Thursday December 25, 2014 @11:44PM (#8798077)
When something is easy at the hardware level but there is no easy way to express it in programs, it suggests a failure of the abstraction. Or more positively, room for improvement. I'd love to see some way that this can be expressed elegantly in C.
Re:Subtraction is not comparison by psyklic on Friday December 26, 2014 @12:11AM (#8798122)
The way that C expresses it elegantly is via the comparison operator. The author is concerned that people try to be clever and use subtraction instead. Here is how the author corrected this error by using comparison operators instead of subtracting (in the tree man page):

    int
    intcmp(struct node *e1, struct node *e2)
    {
  -	  return (e1->i - e2->i);
  +	  return (e1->i < e2->i ? -1 : e1->i > e2->i);
    }
The ternary operator is necessary, because in C a relational operator is evaluated to 0 (false) or 1 (true). So, the author added a special case for returning a negative value (indicating the first argument should be sorted before the second).
Re:Subtraction is not comparison by caf on Friday December 26, 2014 @06:59AM (#8798675)
An alternative without the ternary is

  return (e1->i > e2->i) - (e1->i < e2->i);
Re:Subtraction is not comparison by xmodem on Saturday December 27, 2014 @01:54AM (#8801917)
My C's a little rusty, but isn't the exact integer returned by the comparison undefined? Isn't is just defined as just zero or non-zero?

Therefore i would argue this is just as risky as the original solution, and that it's better to use the ternary operator

Re:Subtraction is not comparison by caf on Saturday December 27, 2014 @08:59PM (#8804837)
No, the relational operators (<, >, <= and >=) are defined to evaluate to either 0 or 1, and the result has type int.

The same is true of the equality operators (= and !=), the logical negation operator (!), and the logical boolean operators (&& and ||).

There are other C idioms based on this, like !!expr to squash a zero-or-non-zero expression down to zero-or-one.

Re:Subtraction is not comparison by mehrdada on Sunday December 28, 2014 @01:19AM (#8805507)
Regardless of the correctness of the code, if it's not obvious and you need to explain it, you probably should not write it (unless you have profile data that makes you do otherwise).

"Programs must be written for people to read, and only incidentally for machines to execute."

Re:Subtraction is not comparison by caf on Sunday December 28, 2014 @04:40AM (#8805865)
I think that particular example should be obvious to anyone whose C isn't "a little rusty".
Re:Subtraction is not comparison by fubarred on Friday December 26, 2014 @02:35AM (#8798364)
Having designed microcoded pipelined processors from gates on up in uni, that's true.

integer cmp x, y often reduces to microcode that effectively runs:

  - 3A: sub x, y, dummy_result (att syntax) <-- most likely^
  - 2A: sub copy_of_x, y (intel syntax)
  - 1A: push x, push y (or y, x), sub, update flags, pop
^ Depends heavily on the microarchitecture, because there's likely all sorts of extra features and ops that can be set in the same microinstruction cycle to implement the macroinstructions.

In signed 2's-complement (mostly everything), zero flag just checks that all result bits to be zero and the less than flag is just the result's MSB (negative result if 1).

Signed and unsigned have their under/overflow issues. Being able to detect that and handle it in code, rather than crashing or silently producing undefined results, can be important, such as safely re-callocing memory (multiply overflow). (There are other issues like pointer/slice index over/underflow too, and these are related problems.)

The issues are usually around whether branch-free code is absolutely necessary or whether predicting branches wrong would incur disastrous pipeline stall/s (greatly depends on the specific use-case, especially inner loops... don't prematurely overoptimize).

I almost feel there needs to be something in-between IR languages like LLVM IR and general-purpose C, that's not assembly but still general-purpose, functional/imperative, static analysis and able to expose math and branching state/options/differences cleanly across a variety platforms without a fugly/verbose syntax.

To make up a pseudoreligion (if it's doesn't look like your preferred Turing-complete language, sorry) at random:

     var x, y int32

     # ... assign x and y somehow here
     begin
       x = x +! y   # or x +=! y
     rescue IntegerOverflowError
       # ... int32.max + int32.max => (0xFFFFFFFF + 0xFFFFFFFF) & 0xFFFFFFFF => 0xFFFFFFFE (-2, int32)
     rescue IntegerUnderflowError
       # ... int32.min + int32.min => (0x7FFFFFFF + 0x7FFFFFFF) & 0xFFFFFFFF => 0xFFFFFFFE (-2, int32)
     end


     var s, t uint64

     # ... assign s and t somehow here
     begin
       s *=! t
     rescue IntegerOverflowError
       # ... uint64.max * uint64.max => ... => 1 (1, uint64)
     end
Re:Subtraction is not comparison by spc476 on Friday December 26, 2014 @04:45AM (#8798544)
In signed 2's compliment architectures (primarily any microprocessor made in the mid 70s or later) that has flags (some RISC based CPUs, like the MIPS, don't have individual flags) will usually have an Overflow flag, (O or V), a Carry flag (C), a Zero flag (Z) and a Sign or Negative flag (S or N---here I'll be using N and V since I have a preference for Motorola CPUs). Yes, internal, a comparison is typically done via a subtraction; the flags are updated, but not the destination register. Now, depending upon how you want to interpret the results, as a signed comparison or unsigned comparison, depends on the flags you check (and the conditional jump (or branch) instructions will do this check for you). The flags that are checked for unsigned comparison are (!f means NOT f, i.e. the flag is not set):

    above or equal: !C
    above:          !C and !Z
    below or equal: C or Z
    below:          C
And the signed comparisons are:

    above or equal: N == V
    above:          N == V and !Z
    below or equal: N != V or Z
    below:          N != V
And, just for completeness, for both signed and unsigned:

    equal:   Z
    unequal: !Z
Re:Subtraction is not comparison by fubarred on Saturday December 27, 2014 @10:37PM (#8805068)
Cool. Brings back memories of implementing various jump instructions and carry prediction adders (do adds twice, pick result based on actual carry later) (I recall I had the smallest microprogram in the course class and it used the fewest microcycles by far... everyone hated me. :)

Would it be nice to be able to access the carry in and carry out as "variables", as well as the full imul/idiv if i.e. 32 args -> 64 bit full-precision results without dropping down into assembly. Perhaps it's unreasonable, but it seems there are few limited differences per generic instruction across processors that assuming they are all each "special unique snowflakes" is obviously untrue FUD.

Understanding architecture / assembly comes in handy to replace high-level branching code with branch-free code to avoid pipeline stalls when branch predictions (eg the branch prediction infrastructure) is guessing incorrectly. Also, being able to go down the stack is really helpful because there are most definitely bugs the way down.

Re:Subtraction is not comparison by ambrop7 on Friday December 26, 2014 @04:16AM (#8798513)
Just do (x>y)-(x<y). It's actually pretty fast: http://stackoverflow.com/questions/10996418/efficient-intege...

Disclaimer: not actually my original invention.

Re:Subtraction is not comparison by pkhuong on Friday December 26, 2014 @09:38AM (#8798916)
That's a good one for integers and if you don't intend the comparator to be inlined. For inlined comparison, I prefer if (x == y) return 0; return (x < y) ? -1 : 1;. The control flow is more transparent and C compilers are usually able to perform the if/if conversion necessary to get the single comparison you'd expect in hand-rolled sorts or searches.
Re:Subtraction is not comparison by Garlef on Friday December 26, 2014 @06:08AM (#8798629)
From a category theoretic point of view, subtraction is a natural candidate for comparison: It is the internal hom-functor in the ordered monoid of integers. One could argue that internal hom functors are the very essence of a "comparison".

The sole problem seems to be that integers in C are not actually integers.

Re:Subtraction is not comparison by emmelaich on Friday December 26, 2014 @09:03AM (#8798840)
I think you're right but it goes deeper. Integers as represented in computers are not integers.
Re:Subtraction is not comparison by TwoBit on Friday December 26, 2014 @12:11AM (#8798121)
This isn't a problem with comparison by subtraction, it's a problem with integer overflow. There's nothing wrong with comparison via subtraction if you account for overflow, which you always need to do in programming.
Re:Subtraction is not comparison by marvy on Friday December 26, 2014 @03:03AM (#8798399)
But the only way to account for overflow is to avoid comparison by subtraction.
Re:Subtraction is not comparison by arielby on Friday December 26, 2014 @05:40AM (#8798607)
You can also do the comparison over larger integers, for example

  int cmp(int x, int y) {
    return (int) (((long)x-(long)y)>>32);
  }
Re:Subtraction is not comparison by Sharlin on Friday December 26, 2014 @11:25AM (#8799194)
In mainstream compilers, even on x64, sizeof(long) == sizeof(int). You need long long instead. Of course, in the general case there's exactly zero guarantee that there even exists an integral type wider than int.
Re:Subtraction is not comparison by TwoBit on Friday December 26, 2014 @12:10PM (#8799356)
The C99 language standard requires support for long long.
Re:Subtraction is not comparison by dezgeg on Friday December 26, 2014 @10:53PM (#8801625)
But an implementation where sizeof(int) == sizeof(long) == sizeof(long long) is allowed by the specification.
Re:Subtraction is not comparison by marvy on Wednesday December 31, 2014 @03:56AM (#8818655)
On a (slightly) more practical level: there exists some largest integer type. You may someday need to sort it. If you want your sort to always work, you can't afford to overflow.
Re:Subtraction is not comparison by salmonellaeater on Friday December 26, 2014 @04:16AM (#8798514)
One way to reduce the chance of this problem occurring is to change the specification of the comparison function to only allow -1, 0, and 1 to be returned (or even better, use enums like LESS, EQUAL, GREATER). This makes doing the wrong thing more cumbersome when implementing a comparison function, but it also makes it easier to implement the actual sort function because jump tables and switch statements become possibilities.
Re:Subtraction is not comparison by marcosdumay on Friday December 26, 2014 @11:14AM (#8799163)
Nobody handles overflow in practice.

There are all kinds of code patterns that are sensitive to overflow, and people do use those patterns because they are simple. A program that does not fail on overflow is something almost illegible, and for most applications the only difference it can make is displaying the correct error message before closing, because the problem domain has no procedure for that.

C programmers are implicitly expected to estimate the value ranges they'll be dealing with, and correctly size their variables.

Re:Subtraction is not comparison by gonzo on Thursday December 25, 2014 @10:15PM (#8797892)
Someone needs to explain to Teddy how the compiler implements subtraction.

Hint: his example is fine (and thus, Ted is wrong).

But, since Ted is OpenBSD.. here come the downvotes.

As the poster below states, the example in the last link of Ted's posting has overflow problems.

Re:Subtraction is not comparison by cremno on Thursday December 25, 2014 @10:39PM (#8797939)
You should read the post again and then open the last link. Then tell us again that the example is fine.
Re:Subtraction is not comparison by gonzo on Thursday December 25, 2014 @10:51PM (#8797975)
Last link is just someone being stupid.
Re:Subtraction is not comparison by cremno on Friday December 26, 2014 @01:05AM (#8798216)
Then why isn't his example stupid, but fine? They're basically doing the same bad thing - subtracting two signed integers without caring about possible overflow.
Re:Subtraction is not comparison by imron on Thursday December 25, 2014 @11:04PM (#8798005)
Doesn't seem so fine to me..

  /*compare.c*/
  #include <stdio.h>
  
  int compare( int x, int y )
  {
      return x - y;
  }
  
  void testCompare( int x, int y )
  {
      if ( compare( x, y ) < 0 )
      {
          printf( "%d is less than %d\n", x, y );
      }
      else
      {
          printf( "%d is greater than or equal to %d\n", x, y );
      }
  }
  
  int main()
  {
      testCompare( 5, 10 );
      testCompare( 10, 5 );
      testCompare( 1987654321, -1987654321 );
      return 0;
  }
$ clang -o compare compare.c

$ ./compare

5 is less than 10

10 is greater than or equal to 5

1987654321 is less than -1987654321

Re:Subtraction is not comparison by industriousthou on Thursday December 25, 2014 @11:43PM (#8798076)
I'm learning javascript, so this sort of issue doesn't really make sense to me, but I gather this is a specific issue with C and it has to do with how large integers are handled. Fair enough, but I'm actually quite curious how you should solve this kind of problem in a low level language and why.
Re:Subtraction is not comparison by SamReidHughes on Friday December 26, 2014 @12:11AM (#8798125)
You just write a function that returns -1, 0, or 1. It's not specific to C, it's in any language that has fixed-size types.

Another reason (a lame one) to return -1, 0, or 1 is that often the caller expects one of those return values for some reason (because the caller is dumb and your comparison function is replacing another one that behaved that way).

Re:Subtraction is not comparison by seivadmas on Thursday December 25, 2014 @09:37PM (#8797814)
WTF? The article states:

  x = 1987654321 and y = -1987654321? Then the difference between them is -319658654 (negative) which proves that x is less than y. That’s less than correct.
Which is completely 100% wrong. Surely the difference would be x - y i.e (1987654321 - (-1987654321)) = (1987654321 + 1987654321) = 3975308642.

Which is perfectly ok, because that's positive and so proves x is greater than y. So this comparison works just fine for negative integers...

Re:Subtraction is not comparison by Scaevolus on Thursday December 25, 2014 @09:46PM (#8797831)
(int32_t)3975308642 == -319658654

Overflow makes intuitive arithmetic do unusual things.

Similarly, using a+b/2 for binary search midpoints is wrong. http://googleresearch.blogspot.com/2006/06/extra-extra-read-...

Re:Subtraction is not comparison by imron on Thursday December 25, 2014 @10:05PM (#8797872)
The writer assumes that the reader understands and knows about how integer overflows work in C.
Re:Subtraction is not comparison by bluedino on Thursday December 25, 2014 @10:08PM (#8797877)
Exactly, don't use subtraction for comparison in C.

The previous commenter may have entered that into say, a Python console where it wouldn't exhibit that behavior.

Re:Subtraction is not comparison by kragen on Friday December 26, 2014 @02:31AM (#8798357)
Don't use subtraction for comparison in any language that uses floating-point (like Lua or JS) or silent overflow (like, typically, C, C++, Java, and C#). There are a few languages, like Python, where integer subtractions that overflow will transparently produce bignums, but they are kind of the exception.
Re:Subtraction is not comparison by seivadmas on Thursday December 25, 2014 @10:16PM (#8797893)
Ah I see the problem. Actually I used a Ruby console.

So what then is the CORRECT way of doing this comparison in C, avoiding potential overflow pitfalls?

Re:Subtraction is not comparison by Too on Thursday December 25, 2014 @10:38PM (#8797935)
The correct way to do a comparison is to use a comparison operator...... If x< y etc...
Re:Subtraction is not comparison by cygx on Thursday December 25, 2014 @10:37PM (#8797932)

    return (x > y) - (x < y);
Re:Subtraction is not comparison by vardump on Thursday December 25, 2014 @10:41PM (#8797947)
That can be very expensive operation. Potentially two branches, not counting return from subroutine.
Re:Subtraction is not comparison by cremno on Thursday December 25, 2014 @10:58PM (#8797992)
If performance really is of concern, qsort() or similar functions probably shouldn't be used. Instead a dedicated function, which allows to choose a specific algorithm (qsort() doesn't have to use quicksort) and also doesn't involve calling a comparison function pointed to by a function pointer, can be used.
Re:Subtraction is not comparison by bluecalm on Friday December 26, 2014 @09:43AM (#8798928)
One interesting thing I have learned recently is that GCC can do inlining through function pointers. That doesn't make your point about dedicated function being better idea for performance but it's one thing "std::sort is faster by design" people often miss.

From GCC documentation:

>>-findirect-inlining Inline also indirect calls that are discovered to be known at compile time thanks to previous inlining. This option has any effect only when inlining itself is turned on by the -finline-functions or -finline-small-functions options.

    Enabled at level -O2.
Re:Subtraction is not comparison by cremno on Friday December 26, 2014 @06:42PM (#8800977)
But GCC likely isn't able to do that for libc functions like qsort(). Maybe if LTO is enabled and libc is linked statically, it might.
Re:Subtraction is not comparison by bluecalm on Saturday December 27, 2014 @04:01AM (#8802120)
I have no idea when it can and when it can't do that to be honest. I tested qsort vs std::sort on my machine on my data and performance was the same (Windows, MinGW, GCC 4.8, -flto enabled) but other people reported different results.
Re:Subtraction is not comparison by azakai on Thursday December 25, 2014 @11:48PM (#8798085)
I would hope that on modern compilers on modern architectures, this would be done with zero branches. That is the case in a quick test on my machine (gcc 4.6.3).
Re:Subtraction is not comparison by vardump on Friday December 26, 2014 @07:50PM (#8801186)
Good if compilers are that smart nowadays. Just a few years ago I did see two branches in a very similar piece of code.
Re:Subtraction is not comparison by userbinator on Friday December 26, 2014 @11:47AM (#8799260)
The simplest way to implement that would be two SETxx instructions and a subtract on 386+, SLT/SGT on MIPS, two subtracts (or one compare and one subtract) and two predicated moves on ARM. No branching required at all.
Re:Subtraction is not comparison by gonzo on Friday December 26, 2014 @01:31AM (#8798268)
Not really. Maybe on an old atom.
Re:Subtraction is not comparison by CamperBob2 on Thursday December 25, 2014 @11:13PM (#8798013)
Exactly, don't use subtraction for comparison in C.... when the difference might exceed 2^31.

For many data types, like time, that will normally never happen, making subtraction the safest no-brainpower-needed approach. If in doubt, use the next larger integer type.

Re:Subtraction is not comparison by tptacek on Friday December 26, 2014 @12:35AM (#8798166)
If you ever want to make a vulnerability researcher drool visibly, have a C programmer say, "that will normally never happen, making ${XXX} the safest no-brainpower-needed approach".
Re:Subtraction is not comparison by arielby on Friday December 26, 2014 @05:43AM (#8798611)
Actually, with time, the sign of (iXX)((uXX)x-(uXX)y) is the "odometer comparison" of x and y, which handles overflow more gracefully than the typical < comparison.
Re:Subtraction is not comparison by CamperBob2 on Friday December 26, 2014 @01:50AM (#8798295)
You know what causes bugs in C (besides memory leaks)? Complex or clever expressions that don't reveal their semantics at first glance. As an example from elsewhere in the thread:

    return (x > y) - (x < y);
Quick. What's that do?

What will the next person who looks at the code think it does?

Re:Subtraction is not comparison by marvy on Friday December 26, 2014 @03:07AM (#8798409)
If it's in a function called "cmp", I think they will guess.
Re:Subtraction is not comparison by CamperBob2 on Friday December 26, 2014 @03:23PM (#8800205)
"I think they will guess."

I think tptacek must be ready to have his bib changed, with all the drooling he must be doing.

Re:Subtraction is not comparison by nitrogen on Friday December 26, 2014 @04:10AM (#8798506)
A C programmer should know that comparisons evaluate to 1 for true and 0 for false.
Re:Subtraction is not comparison by pbsd on Friday December 26, 2014 @06:46AM (#8798661)
C programmers should also be familiar with carry propagation; I see no reason they shouldn't be expected to work out the correctness of

    int cmp(int x, int y) {
      const unsigned a = x;
      const unsigned b = y;
      const unsigned s = sizeof x * 8 - 1;
      return ((b^((b^(b-a))&(a^(b-a))))>>s)&~-((a^((a^(a-b))&(b^(a-b))))>>s)^-((a^((a^(a-b))&(b^(a-b))))>>s)&~-((b^((b^(b-a))&(a^(b-a))))>>s);
    }
Re:Subtraction is not comparison by bluecalm on Friday December 26, 2014 @09:38AM (#8798915)
The difference is that:

    return (x > y) - (x < y); 
Is a common expression which is very efficient (why else would you write anything in C if you don't care about code being fast/efficient?) and what you proposed is neither.
Re:Subtraction is not comparison by CamperBob2 on Friday December 26, 2014 @03:21PM (#8800198)
I write (and read) about 20,000 lines of C(++) a year, and have for the last 20 years. I've never seen that done.
Re:Subtraction is not comparison by SamReidHughes on Friday December 26, 2014 @04:11PM (#8800398)
That doesn't mean the expression is unreadable. It is still extremely straightforward in what it does.
Re:Subtraction is not comparison by dllthomas on Saturday December 27, 2014 @04:32PM (#8804015)
The real difference is that it's simple. There are only 11 symbols in the expression, only two things that can vary, and only three interesting cases. As opposed to 129 symbols and some large number of interesting cases.
Re:Subtraction is not comparison by SamReidHughes on Friday December 26, 2014 @04:14PM (#8800415)
It returns 1 - 0 if x > y, 0 - 1 if x < y, and 0 - 0 if x == y.

Duh.

Re:Subtraction is not comparison by _almosnow on Friday December 26, 2014 @10:37AM (#8799062)
Yeah but that's what happens when people become so adapted to their tools that they forget how real math/world works... and they even have the nerve to stand up for their flawed tools.

I actually like and use C everyday, but it would be aberrant for me to say essentially what the article implies: "A math algorithm is wrong because it doesn't work in C"