Search code examples
cinteger-overflowunsigned-integer

How are the results of intermediate steps in an arithmetic expression stored when all terms in the expression are uint8_t?


The code:

int main(void) {
    uint8_t x = 3;
    uint8_t y = 4;
    uint8_t z = 5;

    uint8_t a = (x - y) / z;
    uint8_t b = x - y;
    printf("a is %d\n", a);
    printf("b is %d\n", b);
    return 0;
}

had the following output:

a is 0
b is 255

I would expect that (3 - 4) / 5 would result in an overflow as (3 - 4) as a uint8_t is 255. When an intermediate step involving unsigned integers results in a negative number, why doesn't it overflow? In what format is the result for (3 - 4) stored before the next step / 5 happens?


Solution

  • One can use a much simpler example to see what's really going on:

    #include <stdio.h>
    #include <stdint.h>
    
    int main(void) {
        uint8_t x = 3;
        uint8_t y = 4;
    
        int a = (x - y);
        printf("a is %d\n", a);    // a is -1
        return 0;
    }
    

    (Live example: http://ideone.com/C3SlIn)

    What you're seeing there is the result of the integer promotions that are performed on the operands to - as part of the usual arithmetic conversions.1 Relevant parts of the definition of integer promotions from the (C99) standard:

    [6.3.1.1] If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.

    An int can indeed represent all values of a uint8_t, so that subtraction is really equivalent to:

    int a = (int)x - (int)y;
    

    In other words, there is no overflow.


    1. But to preempt one common confusion, this behaviour is inherent to how - works; it's not because we're assigning to an int here.