# C++ integer types a tutorial !

The integer types in C++ ** are fundamental types**. A fundamental type , has a mapping to hardware , so the operations performed on a fundamental type , are hardware performed .

Boolean , Characters , and integer types , are called ** the integral types**. The integral types and the floating point types are called the

*arithmetic types**.*

# What are the C++ integer types

An integer type ** is a whole number**, as in

`-1`

or `1`

. A whole number does not contain a fractional part , such as `.2`

.Integer types can either be *unsigned or signed** *. An unsigned integer , can only be non negative , for example `0`

, or `1`

, whereas a signed integer , can be negative , positive , or zero , as in `-1`

, or `3`

.

C++ standards , prior to C++20 , do not specify the *algorithm for representing** *the signed , or unsigned integer types . A signed integer , can be represented , using one’s complements , two’s complement , or sign and magnitude . C++20 , specifies that signed integers , must be represented using two’s complement .

The C++ standards dictates that unsigned arithmetic , such as addition , or subtraction , ** must obey arithmetic **modulo two , to the power

`n`

, this is related to overflow . Overflow happens , when the result is too large to fit in the allocated width , for an integer type .An integer type , can have ** multiple type specifiers**, for example :

`int`

, `signed int`

, and `signed`

, represent the same integer type `int`

, which is signed . This being said , an integer type can be declared in multiple ways , or using multiple keywords.`short int , short `

unsigned short int , unsigned short

int , signed , signed int

unsigned int , unsigned

long int , signed long int , signed long

unsigned long int , unsigned long ,

long long int , signed long long int, signed long long , long long

unsigned long long int , unsigned long long

It is clear ,** from the preceding list**

*, that for signed integer types , it is not necessary to use the keyword*

`signed`

.A signed integer type , and its unsigned version , such as `int`

, and `unsigned int`

, have the ** same storage size and alignment **. Alignment is where in memory , an object can be placed .

An implementation can define ** other integer types**, called the extended integer types . For each extended integer type , a signed , and an unsigned version , must be defined .

# What are the C++ integer types ranges

The C++ standard ** specifies the minimum ranges**, that a signed or an unsigned integer type , might have , an implementation , can define larger ranges .

Before C++ 20 , and because signed integer types could have , a sign and magnitude representation , *the minimum ranges were** :*

Starting C++20 , *the minimum ranges** **are** :*

The minimum ranges of the integral types , as implemented on a given machine , are defined by the** C++ header **

*climits*

.#include<iostream>

#include<climits>int main(void ){

using std::cout;

using std::endl; cout << "CHAR_BIT : " << CHAR_BIT << endl;

/*Print the number of bits in a char .*/ cout << "SCHAR_MIN : " << SCHAR_MIN << endl;

/*Print the minimum value of signed char .*/ cout << "SCHAR_MAX : " << SCHAR_MAX << endl;

/*Print the maximum value of signed char .*/ cout << "UCHAR_MAX : " << UCHAR_MAX << endl;

/*Print the maximum value of unsigned char .*/ cout << "CHAR_MIN : " << CHAR_MIN << endl;

/*Print the minimum value of char .*/ cout << "CHAR_MAX : " << CHAR_MAX << endl;

/*Print the maximum value of char .*/ cout << "MB_LEN_MAX : " << MB_LEN_MAX << endl;

/*Print maximum number of bytes available in

a multibyte character .*/ cout << "SHRT_MIN : " << SHRT_MIN << endl;

/*Print the minimum value of short .*/ cout << "SHRT_MAX : " << SHRT_MAX << endl;

/*Print the maximum value of short .*/ cout << "USHRT_MAX : " << USHRT_MAX << endl;

/*Print the minimum value of unsigned short .*/ cout << "INT_MIN : " << INT_MIN << endl;

/*Print the minimum value of int .*/ cout << "INT_MAX : " << INT_MAX << endl;

/*Print the maximum value of int .*/ cout << "UINT_MAX : " << UINT_MAX << endl;

/*Print the maximum value of unsigned int .*/ cout << "LONG_MIN : " << LONG_MIN << endl;

/*Print the minimum value of long .*/ cout << "LONG_MAX : " << LONG_MAX << endl;

/*Print the maximum value of long .*/ cout << "ULONG_MAX : " << ULONG_MAX << endl;

/*Print the maximum value of unsigned long .*/ cout << "LLONG_MIN : " << LLONG_MIN << endl;

/*Print the minimum value of long long .*/ cout << "LLONG_MAX : " << LLONG_MAX << endl;

/*Print the maximum value of long long .*/ cout << "ULLONG_MAX : " << ULLONG_MAX << endl;

/*Print the maximum value of unsigned long long .*/ }/*Output

CHAR_BIT : 8

SCHAR_MIN : -128

SCHAR_MAX : 127

UCHAR_MAX : 255

CHAR_MIN : -128

CHAR_MAX : 127

MB_LEN_MAX : 6

SHRT_MIN : -32768

SHRT_MAX : 32767

USHRT_MAX : 65535

INT_MIN : -2147483648

INT_MAX : 2147483647

UINT_MAX : 4294967295

LONG_MIN : -9223372036854775808

LONG_MAX : 9223372036854775807

ULONG_MAX : 18446744073709551615

LLONG_MIN : -9223372036854775808

LLONG_MAX : 9223372036854775807

ULLONG_MAX : 18446744073709551615 */

The sizeof operator , returns the ** number of bytes**, which are reserved for a given type , hence it can be used to get the number of bytes , reserved for integer types .

#include<iostream>int main(void ){

using std::cout;

using std::endl; cout << sizeof(short ) << endl;

//Output on this machine : 2 cout << sizeof(int ) << endl;

//Output on this machine : 4 cout << sizeof(long ) << endl ;

//Output on this machine : 8 cout << sizeof(long long ) << endl;

/*Output on this machine : 8 */ }

# C++ integer literals types

An integer literal such as `17`

, *can be written in** *base `2`

, `8`

, `10`

, or `16`

, as follows :

#include<iostream>int main(void ){

using std::cout;

using std::endl; cout << "17 in binary is : " << 0b1'00'01 << endl;

/*Binary literals start by 0B , case insensitive ,

a single quote can be use in any integer

literal for readability .*/ cout << "17 in octal is : " << 021 << endl;

/*Octal literals start by 0 .*/ cout << "17 in hexadecimal is : " << 0x11 << endl;

/*Hexadecimal literals start by

0X , case insensitive .*/ cout << "17 in decimal is : " << 17 << endl;

/*Decimal literals must not start by

0 .*//*Output :

17 in binary is : 17

17 in octal is : 17

17 in hexadecimal is : 17

17 in decimal is : 17 */ }

Since C++ is a typed language , an integer ** literal has a type**. An integer literal is always non negative , the negation operator

`-`

, is applied on the gotten integer literal type .Decimal integer literals , *have a default type of*`int`

, if too large to fit in an `int`

, they will have a `long `

type , if too large to fit in a `long`

, they will have the `long long`

type . If still too large to fit , and the implementation defines extended integer types , they are tried , as described , if still too large , the behavior is implementation defined .

Binary , octal , and hexadecimal integer literals , have *a default type of*`int`

, if too large to fit in an `int`

, they will have a type of `unsigned int`

, if too large to fit in an `unsigned int`

, they will have a type of `long`

, next `unsigned long`

, next `long long`

, next `unsigned long long`

, next if the implementation defines extended integer types , they are tried as stated , if still too large , the behavior is implementation defined .

#include<iostream>int main(void ){

using std::cout;

using std::endl; unsigned int var_i = -2147483648;

/*int on this machine has a range of

[-2147483648 , 2147483647 ] .

2147483648 is larger than INT_MAX ,

2147483648 is a decimal integer literal ,

hence long int is tried .

long int on this machine has a range of

[-9223372036854775808 , 9223372036854775807 ]

, hence 2147483648 is of type long int .

The negation operator is applied on

2147483648 , as such -2147483648 , is gotten .

var_i is an unsigned int , and the gotten

value is a long . Hence the gotten value

is converted first to unsigned long , bits

are kept as is , just reinterpreted , so

the converted value is 2147483648 in

unsigned long , next the converted value

is truncated to an unsigned int , the value

is preserved in truncation . */ cout << var_i << endl ;

/*Output :

2147483648 .*/ var_i = -0x80000000;

/*0x80000000 in hexadecimal is equal to

2147483648 in decimal .

First int is tried .

On this machine it has a range of

[-2147483648 , 2147483647 ] , next

unsigned int is tried .

On this machine , unsigned int has

a range of [0 , 4294967295 ] ,

hence 0x80000000 is of type unsigned

int .

The negation operator is applied ,

modulo 4294967296 is applied , and the

result is 2147483648 .*/ cout << var_i << endl ;

/*Output :

2147483648 .*/ }

The *suffixes **l**, and *

, case insensitive , can be used with an integer literal , to state that it is of type *ll*`long`

, or `long long`

. In such cases , and to determine the type of the integer literal , the compiler starts from `long`

, or `long long`

, depending on the suffix , and try the next types , as described earlier .

The** suffix **

*u*

, case insensitive , can be applied to an integer literal , to state that it is unsigned . In such case , `unsigned int`

is first tried , followed by `unsigned long`

, followed by `unsigned long long`

. If still too large , and the implementation defines extended integer types , the extended integer types are tried , as stated , if still too large , the behavior is implementation defined .The suffix `u`

*can be used with the suffixes** *`l`

, and `ll`

to state that an integer literal is `unsigned long`

, or `unsigned long long`

. In such a case , the compiler tries , the next larger unsigned type , if the literal is too large to fit ,and if no unsigned integer type can fit the literal , then the behavior is implementation defined .

#include<iostream>int main(void ){

using std::cout;

using std::endl; int var_i = 2147483648u;

/*The suffix u is used , as such

the integer literal 2147483648

is of an unsigned integer type .

unsigned int , on this machine

has a range of [0 , 4294967295 ]

, 2147483648 can fit in this range ,

so 2147483648 is of type unsigned int .

var_i is a signed int , as such the

gotten unsigned value , is reinterpreted

as being signed .*/ cout << var_i << endl ;

/*Output

-2147483648 */ var_i = 9223372036854775807L ;

/*9223372036854775807 is suffixed with

L , as such long is first tried .

Long on this machine , has a range

[-9223372036854775808 , 9223372036854775807 ] .

It can hold 9223372036854775807 , so the

integer literal is of type long .

var_i is of type int , as such ,

the gotten long value is truncated ,

and the result is -1 .*/ cout << var_i << endl ;

/*Output :

-1 */ auto var_ul = 1lu;

/*The integer literal 1 , is suffixed

with lu , so it is of the unsigned long

type .

auto is used , as not to write the

type of var_ul , since the integer

literal is of type unsigned long , hence

var_ul , is of type unsigned long .

ul could have been used instead of lu .*/ }

# C++ Standard library integer types

The integer types defined by the C++ standard , are defined to have a least width , as such a least range , so the range of a standard integer type , *is not uniform** *across all implementations .

** For example**, on a

`16`

bit architecture , `int`

have typically a width of `16`

bits , whereas on a `32`

bits architecture , `int`

has typically a width of `32`

bits .The question to ask is as such , *what if what was needed** *, is to have a fixed length width , for an integer type , across all implementations ?

Thestandard library header `cstdint`

, defines** fixed width integer types**, they are :

int8_t

uint8_t

/*Fixed width 8 bits signed and

unsigned integers .*/int16_t

uint16_t

/*Fixed width 16 bits signed and

unsigned integers .*/int32_t

uint32_t

/*Fixed width 32 bits signed and

unsigned integers .*/int64_t

uint64_t

/*Fixed width 64 bits signed and

unsigned integers .*/

The fixed width integer types , ** are optional**, so it is not necessary for an implementation to provide them .

What about , if what was needed , is an integer type , for which a processor in an execution environment , so where the program is being executed , is ** faster to perform operations**, and this integer type , is to be of a minimum length ? The standard library

`cstdint`

header , defines the following integer types , that fit these requirements :int_fast8_t

uint_fast8_t

/*Fastest , signed , unsigned integer

types , that have at least 8 bits .*/int_fast16_t

uint_fast6_t

/*Fastest , signed , unsigned integer

types , that have at least 16 bits .*/int_fast32_t

uint_fast32_t

/*Fastest , signed , unsigned integer

types , that have at least 32 bits .*/int_fast64_t

uint_fast64_t

/*Fastest , signed , unsigned integer

types , that have at least 64 bits .*/

Finally what if what was needed , is to have the** largest integer type** , available on an implementation . To fulfill , this requirement , the

`cstdint`

header defines :`intmax_t`

uintmax_t

/*widest signed , unsigned , integer types

available on an implementation .*/

*Originally published at **https://twiserandom.com** on February 22, 2021.*