Integer Type long long

History

Before long long was officially added to the C++11 standard in 2011, C++ programmers already knew about the long long integer type for a long time. It has been part of the C language since the C99 standard, and many major C++ compilers supported long long for compatibility with C.

As early as 1995, Roland Hartinger first proposed to add long long to C++. At the time, the C committee had not yet considered this type. As a result, the C++ committee was reluctant to add a fundamental type that was not also in C. After long long had been added to C99, Stephen Adamczyk proposed to reconsider its addition to C++ in 2005. Finally, long long was accepted as part of C++ in 2011, more than ten years after it was first included in the C standard.

Bit size

The C++ standard defines long long as an integer type that is at least 64 bits long, but it does not guarantee that long long will always be 64 bits on all platforms. The size of long long can depend on the architecture and the compiler being used. However, most modern platforms do support a 64-bit long long type. To ensure portability and avoid any potential issues, it's best to use the sizeof operator to determine the size of long long on a specific platform.

Remember that in C++, long long is a signed data type, and its corresponding unsigned data type is unsigned long long. It's important to note that long long int and unsigned long long int have the same meaning as long long and unsigned long long, respectively, with the latter forms being shorthand for the former ones.

Literal suffix

The C++ standard defines LL and ULL as literal suffixes for long long and unsigned long long, respectively. When initializing a long long type variable, you can write it like this:

long long x = 65536LL;

The literal suffix LL can be omitted with the same result:

long long x = 65536;

When working with large integer values in C++, it is important to use literal suffixes to ensure that the code runs as intended. For example:

long long x = 65536 << 16; // Value overflows to 0
std::cout << "x = " << x << std::endl;
long long y = 65536LL << 16;
std::cout << "y = " << y << std::endl;

The code long long x = 65536 << 16 performs a bitwise left shift operation on the decimal value 65536 by 16 bits, which can result in an overflow and unexpected behavior.

To prevent overflowing, we should use the LL literal suffix to ensure that the value is treated as a long long data type, as in long long y = 65536LL << 16. This will ensure that the code runs as intended and the value is not unexpectedly truncated or overflowed.

Numerical limits

We should avoid using macro as much as possible for defining the maximum and minimum values:

#define LLONG_MAX 9223372036854775807LL        // long long max value
#define LLONG_MIN (-9223372036854775807LL - 1) // long long min value
#define ULLONG_MAX 0xFFFFFFFFFFFFFFFFULL       // unsigned long long max value

Instead, we should use std::numeric_limits:

#include <iostream>
#include <limits>
#include <cstdio>

int main(int argc, char *argv[])
{
    // Avoid these!
    std::cout << "LLONG_MAX = "  
            << LLONG_MAX  
            << std::endl;

    std::cout << "LLONG_MIN = "  
            << LLONG_MIN  
            << std::endl;

    std::cout << "ULLONG_MAX = " 
            << ULLONG_MAX 
            << std::endl;

    std::printf("LLONG_MAX  = %lld\n", LLONG_MAX);  // format specifier %lld
    std::printf("LLONG_MIN  = %lld\n", LLONG_MIN);  // format specifier %lld
    std::printf("ULLONG_MAX = %llu\n", ULLONG_MAX); // format specifier %llu

    // Use std::numeric_limits
    std::cout << "std::numeric_limits<long long>::max() = " 
            << std::numeric_limits<long long>::max() 
            << std::endl;

    std::cout << "std::numeric_limits<long long>::min() = "
            << std::numeric_limits<long long>::min()
            << std::endl;

    std::cout << "std::numeric_limits<unsigned long long>::max() = "
            << std::numeric_limits<unsigned long long>::max() 
            << std::endl;
}