Why is UINT32_MAX + 1 = 0?

Viewed 831

Consider the following code snippet:

#include <cstdint>
#include <limits>
#include <iostream>

int main(void) 
{
    uint64_t a = UINT32_MAX;
    std::cout << "a: " << a << std::endl;
    ++a;
    std::cout << "a: " << a << std::endl;
    uint64_t b = (UINT32_MAX) + 1;
    std::cout << "b: " << b << std::endl;

    uint64_t c = std::numeric_limits<uint32_t>::max();

    std::cout << "c: " << c << std::endl;

    uint64_t d = std::numeric_limits<uint32_t>::max() + 1; 
    std::cout << "d: " << d << std::endl;
    return 0; 
}

Which gives the following output:

a: 4294967295
a: 4294967296
b: 0
c: 4294967295
d: 0

Why are b and d both 0? I cannot seem to find an explanation for this.

1 Answers

This behaviour is referred to as an overflow. uint32_t takes up 4 bytes or 32 bits of memory. When you use UINT32_MAX you are setting each of the 32 bits to 1 which is the maximum value 4 bytes of memory can represent. 1 is an integer literal which typically takes up 4 bytes of memory too. So you're basically adding 1 to the maximum value 4 bytes can represent. This is how the maximum value looks like in memory:

1111 1111 1111 1111 1111 1111 1111 1111

When you add one to this, there is no more room to represent one greater than the maximum value and hence all bits are set to 0 and back to their minimum value. Although you're assigning to a uint64_t that has twice the capacity of uint32_t, it is only assigned after the addition operation is complete. The addition operation checks the types of both the left and the right operands and this is what decides the type of the result. If atleast one value were of type uint64_t, the other operand would automatically be promoted to uint64_t too. If you do:

 (UINT32_MAX) + (uint64_t)1;

or:

 (unint64_t)(UINT32_MAX) + 1;

, you'll get what you expect. In languages like C#, you can use a checked block to check for overflow and prevent this from happening implicitly.

Related