What are unsigned integers?

While signed integers can represent both positive and negative whole numbers, unsigned integers can only hold non-negative whole numbers (zero and positive integers).

Declaring unsigned integers

To create an unsigned integer, use the unsigned keyword before the type name:

unsigned short us{};
unsigned int ui{};
unsigned long ul{};
unsigned long long ull{};

Range of unsigned integers

A 1-byte unsigned integer has a range of 0 to 255. Compare this with a 1-byte signed integer's range of -128 to 127. Both store 256 unique values, but unsigned integers can represent positive values twice as large.

Size Range
8-bit unsigned 0 to 255
16-bit unsigned 0 to 65,535
32-bit unsigned 0 to 4,294,967,295
64-bit unsigned 0 to 18,446,744,073,709,551,615

An n-bit unsigned integer has a range of 0 to (2^n)-1.

Unsigned integer overflow (wrap-around)

When an unsigned value exceeds its range, the behavior is well-defined: values "wrap around" (called modulo wrapping).

For example, in a 1-byte unsigned integer:

  • 255 is the maximum value
  • 256 wraps to 0
  • 257 wraps to 1

Wrapping works in reverse too:

  • 0 is the minimum value
  • -1 wraps to 255
  • -2 wraps to 254
#include <iostream>

int main()
{
    unsigned short maximum{65535};  // largest 16-bit unsigned value
    std::cout << "maximum was: " << maximum << '\n';

    maximum = 65536;  // exceeds range, wraps to 0
    std::cout << "maximum is now: " << maximum << '\n';

    return 0;
}

Why avoid unsigned integers?

Many developers recommend avoiding unsigned integers in most situations due to two problematic behaviors.

Problem 1: Easy to underflow

With unsigned integers, the lower bound is zero - close to where most values reside, making underflow easy:

#include <iostream>

int main()
{
    unsigned int first{2};
    unsigned int second{3};

    std::cout << first - second << '\n';  // prints 4294967295 (wrong!)

    return 0;
}

Mathematically, 2 - 3 equals -1, but unsigned integers can't represent -1. The result wraps to 4294967295.

Problem 2: Mixing signed and unsigned integers

When an operation involves one signed and one unsigned integer, the signed value converts to unsigned:

#include <iostream>

int main()
{
    signed int negative{-1};
    unsigned int positive{1};

    if (negative < positive)
        std::cout << "-1 is less than 1\n";
    else
        std::cout << "1 is less than -1\n";  // this executes!

    return 0;
}

The -1 converts to 4294967295, so the comparison 4294967295 < 1 is false!

Best Practice
Prefer signed integers over unsigned integers for quantities (even non-negative ones) and mathematical operations. Avoid mixing signed and unsigned integers.

When to use unsigned integers

Despite the above, unsigned integers have legitimate uses:

  • Bit manipulation operations
  • When you need well-defined wrap-around behavior (encryption, random number generation)
  • Array indexing (sometimes unavoidable)
  • Embedded systems or memory-constrained environments