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*Table of Contents

## 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 :*

Type | Negative or zero value | Positive value |
---|---|---|

short | -32767 | 32767 |

unsigned short | 0 | 65535 |

int | -32767 | 32767 |

unsigned int | 0 | 65535 |

long | -2147483647 | 2147483647 |

unsigned long | 0 | 4294967295 |

long long | -9223372036854775807 | 9223372036854775807 |

unsigned long long | 0 | 18446744073709551615 |

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

Type | Negative or zero value | Positive value |
---|---|---|

short | -32768 | 32767 |

unsigned short | 0 | 65535 |

int | -32768 | 32767 |

unsigned int | 0 | 65535 |

long | -2147483648 | 2147483647 |

unsigned long | 0 | 4294967295 |

long long | -9223372036854775808 | 9223372036854775807 |

unsigned long long | 0 | 18446744073709551615 |

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 __ 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 __, case insensitive , can be used with an integer literal , to state that it is of type

`l`

, and `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 __ , case insensitive , can be applied to an integer literal , to state that it is unsigned . In such case ,

`u`

`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 ?

The standard 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 .*/