1

I was reading about techniques to detect overflow in C . one of the examples to show incorrect solution to detect overflow in addition was this one :

/* Determine whether arguments can be added without overflow */
int tadd_ok(int x, int y) {
    int sum = x+y;
    return (sum-x == y) && (sum-y == x);
}

and it said it doesn't work because :

two’s-complement addition forms an abelian group, and so the expression (x+y)-x will evaluate to y regardless of whether or not the addition overflows, and that (x+y)-y will always evaluate to x

What does it exactly mean ? Does it mean that C compiler replace sum with x+y ?
To figure out what is it saying I even traced assembly code of the program, but there was no sign of replacement .

Update : The essence of my question is, does GCC evaluates an expression without calculating it ?
This is NOT a question about two's complement.
You can see a sample output in here .

Rsh
  • 7,214
  • 5
  • 36
  • 45
  • 5
    "Abelian group" is just a fancy mathematical term for a "ring". Two's complement integers with the usual overflow behavior forms a "ring" of integers. When you add/subtract, you shift along the ring. So regardless of overflow, you can always shift back in the opposite direction. – Mysticial Jul 13 '12 at 18:45
  • @Mysticial Although two's-complement is a machine implementation and I do not believe the behavior is defined as such in C "proper" .. –  Jul 13 '12 at 18:46
  • @pst Indeed that's true. But I didn't want to confuse the OP too much. – Mysticial Jul 13 '12 at 18:47
  • 1
    Do you mean to ask that, seeing as signed overflow gives unspecified results, this "broken overflow detection method" might accidentally be made to work due to an optimization that uses the unspecifiedness? – harold Jul 13 '12 at 19:14
  • How is this different to your [previous question](http://stackoverflow.com/questions/11460288/twos-complement-addition-overflow-in-c/11464365#11464365)? – Oliver Charlesworth Jul 13 '12 at 19:30
  • No, This one, always returns true (for `int`). I want to see the does compiler( or optimizer ) interfere in evaluating the value of this expression or not . something like : `(3+1e20)-1e20` and `3+(1e20-1e20)` – Rsh Jul 13 '12 at 19:31
  • @Charlesworth back there solutions focused on difference between `short` and `int` in the function, but that was not my problem ! – Rsh Jul 13 '12 at 19:32
  • 2
    @ArashThr: then I'm not sure what you're asking here! Compilers **may** simplify arithmetic expressions, but they're not **obliged** to. I'm not sure what this has to do with abelian groups. – Oliver Charlesworth Jul 13 '12 at 19:34
  • Yes, absolutely, with optimizations turned on gcc and other compilers, can and will pre-compute a result and not actually code the operations in the assembly/machine code. a = 4 + 5; the compiler is going to change that to a = 9; you wont see the addition in the output of the compiler. – old_timer Jul 13 '12 at 21:03
  • 1
    as Oli says, they are not required or obliged, but since you asked about gcc, it typically does and I have seen it replace many lines of code and a very long loop with a single result. because it was all static stuff, it precomputed the result and simply filled the register with a result, tons of code removed. – old_timer Jul 13 '12 at 21:04
  • 1
    The question update deserves a change of the title, tags and a better wording as the question starts talking about 2's complement overflow and ends up asking about gcc behavior. – Alexey Frunze Jul 13 '12 at 21:40

5 Answers5

2

If you take a trivial example of 4 (0b0100) + 5 (0b0101) you can see that the unsigned sum should be 9 (1001) which is actually -7 in two's complement. If you then take that sum (0b1001) and subtract 4 from it using two's complement arithmetic:

    0b1001 - 0b0100 = 0b1001 + 2s_complement(0b0100) = 0b1001 + 0b1100 = 0b1_0101 

you end up with 0101 which is 5 (you drop the overflowing most significant 1 during a 2's complement operation). Subtracting 5 from the sum equals 4:

    0b1001 - 0b0101 = 0b1001 + 2s_complement(0b0101) = 0b1001 + 0b1011 = 0b1_0100

This satisfies the c code you provided but still resulted in an overflow.

From wikipedia's article on two's complement:

Two's complement    Decimal
0111                 7
0110                 6
0101                 5
0100                 4
0011                 3
0010                 2
0001                 1
0000                 0
1111                −1
1110                −2
1101                −3
1100                −4
1011                −5
1010                −6
1001                −7
1000                −8

Update:
To demonstrate your INT_MAX example using my trivial 4 bit integer system with INT_MAX = 7 we can see the same result as your c code.

    7 + 7 (0b0111 + 0b0111) = 0b1110 (-2 in two's complement)

Just like my example above, subtracting, sum - 7 will equal 7.

    0b1110 - 0b0111 = 0b1110 + 2s_complement(0b0111) = 0b1110 + 0b1001 = 0b1_0111
CraigTeegarden
  • 8,173
  • 8
  • 38
  • 43
0

I might be wrong, but this seems to me like a simple overflow check... if sum had overflowed, sum-x could not be equal to y

something more: parameters are passed in the stack to the function, sum is a local variable and it's on the stack too. The compiler is likely to put sum=0, sum+x and them sum+y (stack location + another stack location) into the sum location. For the return statement it's likely to put the result into another temporary location by duplicating the sum variable

Johnny Pauling
  • 12,701
  • 18
  • 65
  • 108
0

Looks like the error that would cause an incorrect result would also affect both sides of the subsequent test, and so the test would never fail.

gbarry
  • 10,352
  • 6
  • 33
  • 43
0

It's possible that if there is optimization on your compiler (i.e. -02) then this function would not work. However, if there is no optimization, I believe this would work correctly.

Sharon
  • 1
  • The reverse actually. This almost certainly won't work (assuming sane hardware), unless the compiler did something funny. – harold Jul 13 '12 at 19:25
0

With respect to your posted code, it will not work on all platforms because some platforms will implicitly wrap around overflows. The expression will evaluate in the following way:

int x    = 0xFFFFFFF0;
int y    = 0x00000020;

int sum  = x + y;   // value of sum is 0x00000010
                    // would be 0x100000010, but highest order bit is dropped

int diff = sum - y; // value of diff is 0xFFFFFFF0
                    // equal to x

The domain is closed, i.e. an overflow will wrap around in a predictable and reversible way. You cannot rely on this method to check for an overflow.

That said, this behavior is platform specific. Some processors will do it this way, and others will return the closest possible representable value (i.e. sum would equal 0xFFFFFFFF), and in this case, it would work as expected.

The best way to check for an overflow in C would be to use an inline assembly statement to push the flags onto the stack, pop them into a register, and return the value. One of the flags will be set if an overflow occurred on the last mathematical operation. I have done it this way before but don't remember how to do it. See the following pages for resources and help:

http://en.wikipedia.org/wiki/FLAGS_register_(computing)

Read flag register from C program

Community
  • 1
  • 1
Wug
  • 12,956
  • 4
  • 34
  • 54