CS253: Software Development with C++

Spring 2022

Fixed Width Integers

Show Lecture.FixedWidthIntegers as a slide show.

CS253 Fixed Width Integers

Inclusion

To use fixed-width integers, you need to:

    
#include <cstdint>

The Rules

Unlike languages such as Java, C++ integer sizes aren’t fixed:

This Computer

char c = -1;
cout << "char is " << (c<0 ? "signed\n" : "unsigned\n")
     << "char signed: " << boolalpha
     << numeric_limits<char>::is_signed    << '\n'
     << "char: "      << sizeof(char)      << '\n'
     << "short: "     << sizeof(short)     << '\n'
     << "int: "       << sizeof(int)       << '\n'
     << "long: "      << sizeof(long)      << '\n'
     << "long long: " << sizeof(long long) << '\n';
char is signed
char signed: true
char: 1
short: 2
int: 4
long: 8
long long: 8

Rationale

The intention is that int is the natural size for integer values on this computer. The C/C++ languages are quite concerned about efficiency, and int operations must be quick. It would be silly to have a 64-bit computer doing extra work to fake overflow in a 16-bit int.

On 16-bit computers, int was 16 bits. int has been stuck at 32 bits for too long, maybe due to bad code that assumes 32-bit ints.

Indeed, occasionally, I want an integer type that is exactly 32 bits. Perhaps I’m simulating hardware that has 32-bit registers. Perhaps I want reproducible overflow characteristics for a random number generator.

Poor Solution

#ifdef STUPID_16_BIT_COMPUTER
    typedef long int32;
#elif MAC_COMPUTER
    typedef int int32;
#elif HP_COMPUTER
    typedef long long int32;
#else
    #error Cannot figure out 32-bit int size.
#endif
c.cc:8: error: #error Cannot figure out 32-bit int size.

Fragile!

Better Solution

Rather than guessing sizes, based on compiler version and machine type, it would be great if we could convince the compiler implementors (who actually know) to tell us how to get integers of various sizes.

That’s what the <cstdint> header file is for.

<cstdint>

<cstdint> (alias <stdint.h> in C) provides aliases (typedefs):

and all of those with a leading u for unsigned: uint8_t, …

Explanation

Exact sizes (int8_t, …)
These are the exact size—no smaller, no bigger.
Fastest at least this big (int_fast8_t, …)
The fastest type that can hold at least this many bits. Speed matters; space doesn’t. On a 64-bit computer that has to do extra work to simulate 16-bit overflow, it would be faster to use all 64 bits.
Smallest at least this big (int_least8_t, …)
Space matters; speed doesn’t.
Biggest signed integer (intmax_t)
I just want the biggest range this computer can offer.
Holds a pointer (intptr_t)
I want to hold a pointer, or perhaps the difference between two pointers.

Exact Types are Optional

Everything Else is Mandatory

This Computer

cout << sizeof(int8_t)        << '\n'
     << sizeof(int16_t)       << '\n'
     << sizeof(int32_t)       << '\n'
     << sizeof(int64_t)       << '\n'
                              << '\n'
     << sizeof(int_fast8_t)   << '\n'
     << sizeof(int_fast16_t)  << '\n'
     << sizeof(int_fast32_t)  << '\n'
     << sizeof(int_fast64_t)  << '\n'
                              << '\n'
     << sizeof(int_least8_t)  << '\n'
     << sizeof(int_least16_t) << '\n'
     << sizeof(int_least32_t) << '\n'
     << sizeof(int_least64_t) << '\n'
                              << '\n'
     << sizeof(intmax_t)      << '\n'
     << sizeof(intptr_t)      << '\n';
1
2
4
8

1
8
8
8

1
2
4
8

8
8

Data Models

One speaks of a Data Model, e.g., LP64, which means that Longs and Pointers are 64 bits wide.

TypeLP32ILP32LLP64LP64ILP64SILP64
short161616161664
int163232326464
long323232646464
long long??64646464
pointer, size_t323264646464
#ifdef __LP64__  // non-standard
    cout << "LP64\n";
#endif
LP64