Hacker News new | ask | show | jobs
by imron 4191 days ago
The writer assumes that the reader understands and knows about how integer overflows work in C.
1 comments

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.

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.
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?

The correct way to do a comparison is to use a comparison operator...... If x< y etc...

    return (x > y) - (x < y);
That can be very expensive operation. Potentially two branches, not counting return from subroutine.
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.
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.
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).
Good if compilers are that smart nowadays. Just a few years ago I did see two branches in a very similar piece of code.
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.
Not really. Maybe on an old atom.
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.

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".
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.
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?

If it's in a function called "cmp", I think they will guess.
"I think they will guess."

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

A C programmer should know that comparisons evaluate to 1 for true and 0 for false.
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);
    }
It returns 1 - 0 if x > y, 0 - 1 if x < y, and 0 - 0 if x == y.

Duh.