An integer is an integral type that can represent positive and negative whole numbers, including 0 (e.g. -2, -1, 0, 1, 2). C++ has *4* different fundamental integer types available for use:

Type | Minimum Size | Note |
---|---|---|

short | 16 bits | |

int | 16 bits | Typically 32 bits on modern architectures |

long | 32 bits | |

long long | 64 bits |

The key difference between the various integer types is that they have varying sizes -- the larger integers can hold bigger numbers.

A reminder

C++ only guarantees that integers will have a certain minimum size, not that they will have a specific size. See lesson 4.3 -- Object sizes and the sizeof operator for information on how to determine how large each type is on your machine.

Signed integers

When writing negative numbers in everyday life, we use a negative sign. For example, *-3* means “negative 3”. We’d also typically recognize *+3* as “positive 3” (though common convention dictates that we typically omit plus prefixes). This attribute of being positive, negative, or zero is called the number’s sign.

By default, integers are signed, which means the number’s sign is preserved. Therefore, a signed integer can hold both positive and negative numbers (and 0).

In this lesson, we’ll focus on signed integers. We’ll discuss unsigned integers (which can only hold non-negative numbers) in the next lesson.

Defining signed integers

Here is the preferred way to define the four types of signed integers:

1 2 3 4 |
short s; int i; long l; long long ll; |

All of the integers (except int) can take an optional *int* suffix:

1 2 3 |
short int si; long int li; long long int lli; |

This suffix should not be used. In addition to being more typing, adding the *int* suffix makes the type harder to distinguish from variables of type *int*. This can lead to mistakes if the short or long modifier is inadvertently missed.

The integer types can also take an optional *signed* keyword, which by convention is typically placed before the type name:

1 2 3 4 |
signed short ss; signed int si; signed long sl; signed long long sll; |

However, this keyword should not be used, as it is redundant, since integers are signed by default.

Best practice

Prefer the shorthand types that do not use the *int* suffix or signed prefix.

Signed integer ranges

As you learned in the last section, a variable with *n* bits can hold 2^{n} possible values. But which specific values? We call the set of specific values that a data type can hold its range. The range of an integer variable is determined by two factors: its size (in bits), and whether it is signed or not.

By definition, an 8-bit signed integer has a range of -128 to 127. This means a signed integer can store any integer value between -128 and 127 (inclusive) safely.

As an aside...

Math time: an 8-bit integer contains 8 bits. 2^{8} is 256, so an 8-bit integer can hold 256 possible values. There are 256 possible values between -128 to 127, inclusive.

Here’s a table containing the range of signed integers of different sizes:

Size/Type | Range |
---|---|

8 bit signed | -128 to 127 |

16 bit signed | -32,768 to 32,767 |

32 bit signed | -2,147,483,648 to 2,147,483,647 |

64 bit signed | -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 |

For the math inclined, an n-bit signed variable has a range of -(2^{n-1}) to 2^{n-1}-1.

For the non-math inclined… use the table. :)

Integer overflow

What happens if we try to assign the value *280* to an 8-bit signed integer? This number is outside the range that a 8-bit signed integer can hold. The number 280 requires 9 bits (plus 1 sign bit) to be represented, but we only have 7 bits (plus 1 sign bit) available in a 8-bit signed integer.

Integer overflow (often called *overflow* for short) occurs when we try to store a value that is outside the range of the type. Essentially, the number we are trying to store requires more bits to represent than the object has available. In such a case, data is lost because the object doesn’t have enough memory to store everything.

In the case of signed integers, which bits are lost is not well defined, thus signed integer overflow leads to undefined behavior.

Warning

Signed integer overflow will result in undefined behavior.

In general, overflow results in information being lost, which is almost never desirable. If there is *any* suspicion that an object might need to store a value that falls outside its range, use a type with a bigger range!

Integer division

When dividing two integers, C++ works like you’d expect when the quotient is a whole number:

1 2 3 4 5 6 7 |
#include <iostream> int main() { std::cout << 20 / 4; return 0; } |

This produces the expected result:

5

But let’s look at what happens when integer division causes a fractional result:

1 2 3 4 5 6 7 |
#include <iostream> int main() { std::cout << 8 / 5; return 0; } |

This produces a possibly unexpected result:

1

When doing division with two integers (called integer division), C++ always produces an integer result. Since integers can’t hold fractional values, any fractional portion is simply dropped (not rounded!).

Taking a closer look at the above example, 8 / 5 produces the value 1.6. The fractional part (0.6) is dropped, and the result of 1 remains.

Similarly, -8 / 5 results in the value -1.

Warning

Be careful when using integer division, as you will lose any fractional parts of the quotient. However, if it’s what you want, integer division is safe to use, as the results are predictable.

If fractional results are desired, we show a method to do this in lesson 5.2 -- Arithmetic operators.

4.5 -- Unsigned integers, and why to avoid them |

Index |

4.3 -- Object sizes and the sizeof operator |

Hi!

Congrats for this amazing tutorial! I think that in the section of "Integer overflow" would be educative to mention real life cases such as the bug of the Ariane 5: https://www.bbc.com/future/article/20150505-the-numbers-that-lead-to-disaster

Hi, can someone give me some guidance on this. I understand that 1 byte (8 bits) can hold 256 POSSIBLE values, but once this is stored in memory whatever the sequence it is (01101010) it will only be 8 bits but in the lesson it says it can HOLD 256 different values but surely its 256 possible values ? ??

Yes, possible values is a better phrasing. Article updated.

Thanks.

Hi to anyone who might respond, I do not understand this lesson. Forgive me if I have misunderstood, been ignorant about the lesson or if the reason is too trivial.

Can someone enlighten me why signed is used if the default is already signed?

I also have another question, I think it would be explained in a future lesson but, what is short/long/int used for? and what makes them different from each other?

In the author's system short and int had the same size, in my system long and int has the same size.

OK.. Would you drive a tractor trailer to go to the corner store to buy a single carton of milk? Would choose a bicycle as a moving vehicle of choice when buying a new house? These are ridiculous but they make the point that in this exercise, size matters.

OK so why one type over another... well if you count in binary to until you have 11111111 (8 bits/1 byte), you would have a value of 255 because 8 "on" bits is the same number as 255. Can I fit the value 257 in those same 8 bit? No, I'm out of bits so I need a second byte,.. I need 16 bits to make the number 257 in binary. So the different types are different sizes (numbers of bits).

What about signs.. well, the machine has to use 1 of those 8 bits for to tell if its + or -. So if I could have the value 255 in 8 bits, than if I have to give up a bit for the +/- sign I loose the MSB (Most significant bit) and I can now only count as high as 127 BUT I can now count as low as -127 too.

So the 8 bits unsigned allows me to go from 0 to 255 but if I have to give up a bit for the sign I am limited to -127 to +127. If I suddenly need to store the value 257 like before, I'm going to need more bits.

Simplify.. pretend we're talking about 1 bit.. I can only count from 0 - 1 and I cant show sign. If I add a bit (11) now I can count from 0 to 3 with no sign OR I can give up a bit for the sign and have -1 to +1. If I try to count to 4 I need to have more bits and if I need to be able to go to -4 I'll need to have another bit for the sign.

That's how it works. It's called "Two's compliment".

Think in terms of binary.. bits and how big of a number you can MAKE with the bits you have in a given type.

guys correct me if im wrong or if there's going to be future issues with the idea but....

in computing for the range of a bit(s) or byte(s) where we use the formula -(2n-1) to 2n-1-1

it can get confusing for us beginners or difficult to remember.

so instead of trying to memorize this formula, what i did is only remember the part on how to get the possible values of the bit(s) or byte(s)

once i get that raw value i can then divide it into 2 and simply tweak the result to (add - sign and subtract 1) to get the range of a signed integer.

i realize this looked like more troublesome or have more work than simply solving -(2n-1) to 2n-1-1

but for some reason it seems easier to remember than remembering -(2n-1) to 2n-1-1

basically i memorize ( 2n-1 / 2 ) then add - sign and subtract 1 from the result.

for example 4bytes or 32bits signed integer ( 4,294,967,296 / 2 ) = 2,147,483,648

i simply add - sign to that number to get range MIN -2,147,483,648

then subtract 1 from that number to get range MAX 2,147,483,647

so -2,147,483,648 to 2,147,483,647

seems like a lot of work come to think of it. but i find it easier to remember 2n-1 / 2 than -(2n-1) to 2n-1-1

EDIT : nvm now looking at it i realize it is indeed more work than remembering the proper formula.