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

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

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 .*/
```