Monday, February 7, 2011

C: Implicit casting and interger overflowing in the evaluation of expressions

Lets take the code

int a, b, c;
...
if ((a + b) > C)

If the values of a and b add to exceed the max value of an int will the integrity of the comparison be compromised? I was thinking that there might be an implicit up cast or overflow bit checked and factored into the evaluation of this expression.

  • C will do no such thing. It will silently overflow and lead to a possibly incorrect comparison. You can up-cast yourself, but it will not be done automatically.

    James D : Agree! No self-respecting C compiler will ever do this. SILENTLY OVERFLOW BABY! And we wouldn't have it any other way. This is what's great about C, after all.
    From hazzen
  • I believe this might be platform specific. Check the C documentation on how overflows are handled...

    Ah, yes, and the upcast will not happen automatically...

  • See section 2.7, Type Conversions in the K&R book

    From cwick
  • A test confirms that GCC 4.2.3 will simply compare with the overflowed result:

    #include <stdio.h>
    
    int main()
    {
        int a, b, c;
    
        a = 2000000000;
        b = 2000000000;
        c = 2100000000;
    
        printf("%d + %d = %d\n", a, b, a+b);
        if ((a + b) > c)
        {
            printf("%d + %d > %d\n", a, b, c);
        }
        else
        {
            printf("%d + %d < %d\n", a, b, c);
        }
        return 0;
    }
    

    Displays the following:

    2000000000 + 2000000000 = -294967296
    2000000000 + 2000000000 < 2100000000
    
  • If upcasting doesn't gain you any bits (there's no guarantee that sizeof(long)>sizeof(int) in C), you can use conditions like the ones below to compare and check for overflow—upcasting is almost certainly faster if you can use it, though.

    #if !defined(__GNUC__) || __GNUC__<2 || (__GNUC__==2 && __GNUC_MINOR__<96)
    #   define unlikely(x) (x)
    #else
    #   define unlikely(x)     (__extension__ (__builtin_expect(!!(x), 0)))
    #endif
    
    /* ----------
     * Signed comparison (signed char, short, int, long, long long)
     * Checks for overflow off the top end of the range, in which case a+b must
     * be >c.  If it overflows off the bottom, a+b < everything in the range. */
    if(a+b>c || unlikely(a>=0 && b>=0 && unlikely(a+b<0)))
        ...
    
    /* ----------
     * Unsigned comparison (unsigned char, unsigned short, unsigned, etc.)
     * Checks to see if the sum wrapped around, since the sum of any two natural
     * numbers must be >= both numbers. */
    if(a+b>c || unlikely(a+b<a))
        ...
    
    /* ----------
     * To generate code for the above only when necessary: */
    if(sizeof(long)>sizeof(int) ? ((long)a+b>c)
           : (a+b>c || unlikely(a>=0 && b>=0 && unlikely(a+b<0)))
        ...
    

    Great candidates for macros or inline functions. You can pull the "unlikely"s if you want, but they can help shrink and speed up the code GCC generates.

0 comments:

Post a Comment