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 2n different 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. 28 is 256, so an 8-bit integer can hold 256 different values. There are 256 different 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 -(2n-1) to 2n-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.
![]() |
![]() |
![]() |
Question 1:In the case of signed integers, which bits are lost is not well defined, thus signed integer overflow leads to undefined behavior.
As you say, signed integers are positive or negative integers, right?
hello alex i wanna ask what this paragraph (For the math inclined, an n-bit signed variable has a range of -(2n-1) to 2n-1-1.) means? I am a foreigner so I don't understand the meaning of it. Please explain again to me.For the math inclined, an n-bit signed variable has a range of -Can you explain this passage using any other words to explain? I can't translate it that makes sense to me
For the people who like math: A signed integer variable with n bits has a range of -(2^(2-1)) to 2^(n-1)-1
What is the meaning of "signed"?
I've gone through the article but it doesn't state what signed means.
Is signed supposed to mean both positive and negative, while unsigned means from 0 to positive?
"signed" means "to have a sign", ie. positive and negative.
"unsigned" integers don't have a sign, they're always non-negative.
So, I tried to code to understand the overflow and put the code in an infinite loop. When I put the value of 'us' > 65535, it doesn't ask for input next time and jumps to while(). Is this undefined behavior OR if this can be resolved with flushing stdin (tried, but didn't work) ?
I tried to look into 'stdin' as well, but couldn't find anything. Though 65535 is upper limit for short int.
Can you please advise:
When you enter a value that's larger than the maximum value that can be stored, the maximum value will be stored. Additionally, `std::cin` enters a failed state, which is what's preventing you from using `std::cin` after the failed extraction. You can reset `std::cin` by calling `std::cin.clear()`.
I spotted 2-3 errors:
A byte, or in C++ a char, has to be at least 8 bit, but can be larger. I think someone mixed up byte and octet.
SCHAR_MIN is -127 or smaller not -128 or smaller, but it is -128 on most systems. Similar for SHORT_MIN, INT_MIN, LONG_MIN and LLONG_MIN. The C++ standard does not require 2-complement.
A int does not need to have at least 2 byte, it can have 1, when CHAR_BIT>=16. Not common but there are systems where sizeof(int)==1
> I think someone mixed up byte and octet.
> A int does not need to have at least 2 byte
I updated the lesson to use bits rather than bytes to avoid this mix-up. Thanks for pointing it out!
Let me whoop out my textbook for the rest
> SCHAR_MIN is -127 [...]
"The range of representable values for a signed integer type is -2^(N-1) to 2^(N-1)-1 (inclusive), where N is called the width of the type."
n4835 § 6.8.1 (1)
A signed char has a minimum width of 8 bits according to n4835 § 6.8.1 Table 10
-2^(8-1)=-128
> The C++ standard does not require 2-complement
It does since C++20: n4835 § 6.8.1 (3)
If using optinal suffix is not encourage. Why would it allows programer at the first place? Is there any particular reason other than it's redundant?
Hi nascardriver/Alex, I think that a much simpler way to explain the ranges might be the following:
Let's say we have a 1 byte signed [range: -128 to 127]
We know that 1 byte = 8 bits
We also know that to find how many values the bits can hold, we can do 2 e 8.
2e8 = 256. All we need to do now is 256 / 2, which is 128. That's it: we now have the (negative minimum) number, which is -128. The positive number is the same number as per the negative, just -1. So if -128, the max positive will be 127. If -130, the max positive will be 129 and so on.
I think this is much faster to remember thanks to the previous lessons!
"An integer is a integral type.."
shouldn't be "An integer is AN integral type"?
It should. Thanks :)
Hello,in the last lessson you said those size of variables vary based on machine and compiler,but you said that those size shown in the table are the minimum size.in this lesson you talked about a signed integer with 1byte size ? the minimum size of int is 2bytes,so does that mean if the value we assign to the variable is so low like "1" its size is reduced and can be even 1 ?
"integer" does not mean "int". `int` is a type, that's what you use in C++. "integer" is a mathematical term, meaning whole numbers, or numbers without a fractional part.
my sizeof program in visual studio gives the same result as your display. But in Code::Block running on the same machine in windows installed with mingw gives the follwing result: Could you explain why? Only difference is the Long Double ?
bool: 1 bytes
char: 1 bytes
wchar_t: 2 bytes
char16_t: 2 bytes
char32_t: 4 bytes
short: 2 bytes
int: 4 bytes
long: 4 bytes
long long: 8 bytes
float: 4 bytes
double: 8 bytes
long double: 16 bytes
Implementations (Compiler+Standard library) are free to do what they want as long as they stay within the limitations set by the standard. The standard dictates only minimum sizes for types and relations between the sizes (eg. `int` is at least as wide as `short`). Implementations are free to make them wider.
if i convert 8 to binary code it'll be 00111000 and that's not 3 bits it's 4 bits , i didn't get it please someone help me :(
8 in binary is 1000, that's 4 bits. A bit isn't a 1, a bit is a 0 or a 1.
but if a bit has 2 digits which is 0 or 1 that means a one bit has 2 digits and there are 4 digits in 1000 doesn't that mean that 1000 is 2 bits ?
The binary number you put (00111000) is 56 in binary. This is 8 bits long
8 in binary is (1000)or(00001000) --> the only difference being the first example is 4 bits long and the second is 8 bits long
^ ^
4bits 8bits
bits and digits are the same in binary.
has 4 bits/digits. 2 bits/digits are 1, the other 2 are 0.
yeah sorry i was confused cuz someone told that 2 digits (0 and 1 or 1 and 0) represents a 1 bit but after a lot of search i just found out that a bit can either be 0 or 1 , sorry i'm trying to delete my comments but it's too late :)
Nah, the comments can stay. If someone else has the same misunderstanding as you, this conversation will help them :)
why this compiles:
but this doesn't:
You can only initialize variables at their declaration. When you want to assign to them, you have to use `=`.
does this mean that if I wanted to change the value assigned to a precise variable, I'd have to use ’=’ each time? (I'm following religiously the tutorials, so there may other methods for that later)
Yes, `=` is for assignments.
It is given in this tutorial that the capacity of the signed integer is 256 ranging from -128 to 127, a/c to me it should be from -127 to 127, means 255 numbers but 256 representations as because zero can be represented as 00000000, 10000000.
8 bit binary 1000 0000 is -128 decimal. Only 0000 0000 is 0.
Why? as for the 8-bit representation of a binary number, the first digit will be used for deciding its sign and the rest of the seven digits will be used for the number itself. So, 10000000 represents 0. If I'm wrong please correct me with a brief explanation.
Negative numbers are represented in two's complement. This is covered in lesson O.4.
Okay, I thought we're using sign-bit representation. Thanks!!!
Can I just note here - this is lesson O.4 Bit Manipulation not lesson O.4 Introduction.
One is 'O' (letter), the other '0' (number). Let's hope we get around rearranging the lessons soon.
hmm just for my satisfaction xD
Those are the maximum sizes on your system, but they can vary, causing your code to not compile (Because the numbers are too large for the type). A portable solution involves using `std::numeric_limits`
An editorial suggestion: "The number 280 requires 9 bits to represent (plus the sign bit), but we only have 7 bits (plus the sign bit) available in a 1-byte integer."
Lesson updated, thanks!
What is the difference between long and int on modern architectures? They have the same size.
If long and int have the same size on your architecture then they function identically.
Hi Alex and Nascardriver!
I have 3 questions here:
1. signed integer can hold negative number, but what about the negative sign?
2. can string type overflow?
3. How to understand the difference between overflow and wrap-around (modulo wrapping)?
1.
The internal representation of integers is implementation defined. Most compilers use two's complement (covered in the lesson about binary conversion).
2.
You could run out of memory.
3.
overflow: You add a number, the addition is performed, all binary digits that don't fit into the type are dropped.
wrap around: You add a number, if it doesn't fit into your type, its remainder is stored.
signed numbers overflow (I don't know if this is well defined), unsigned numbers explicitly wrap around.
Thank you for reply!
1. the range of type is -2^(n-1) to 2^(n-1)-1. Why n-1?
2. I don't get it
Your total number of values is `2^n`, where `n` is the number of bits. We need 1 sign bit, leaving `n-1` bits for the number, thus we have `2^(n-1)` negative- and `2^(n-1)` non-negative numbers. The non-negative numbers include `0`, so the maximum value is `2^(n-1) - 1`. The negative numbers remain unchanged, the minimum value is `-(2^(n-1))`.
Binary numbers are covered in lesson O.3.7.
Alex,
Firstly, this site is AMAZING! Thank you.
Secondly as a simple editing note, do you mean to use the word integral in the paragraph below? It seems like you meant to use integer instead.
Pretty simple, right? We can infer that operator sizeof returns an integral value -- but what type of integer is that value? An int? A short? The answer is that sizeof (and many functions that return a size or length value) return a value of type “size_t”. size_t is an unsigned, integral value that is typically used to represent the size or length of objects.
Yes, I think it makes more sense to use "integer" than "integral" here. Thanks for the feedback!
Hello dear,
What is the ' size_t ' exactly ?
I've read the above texts, but I haven't understood what the 'size_t' does exactly.
Can it be a data type ?
Output is 0, it's a 4-byte unsigned type.
Hi!
See my answer here
https://www.learncpp.com/cpp-tutorial/24-integers/comment-page-3/#comment-390187
> it's a 4-byte unsigned type
On your system, yes. It might not be somewhere else.
> Is that [] considered overflow
Yes
> Is that technically considered overflow
No. Unsigned types don't overflow, they explicitly wrap around.
Integer overflow is defined by Wikipedia as, "[occuring] when an arithmetic operation attempts to create a numeric value that is outside of the range that can be represented with a given number of digits."
Based on that definition, unsigned types do overflow. They just exhibit guaranteed wrap-around behavior when they do.
The range of representable values for the unsigned type is 0 to 2 N − 1
(inclusive); arithmetic for the unsigned type is performed modulo 2 N . [Note: Unsigned arithmetic does not overflow. Overflow for signed arithmetic yields undefined behavior (7.1). — end note]
N4791 § 6.7.1 2
So if i understand correctly (from reading multiple definitions of size_t), size_t returns the maximum width an integer data type can store in bytes in memory? and this is used to understand the maximum value that an integer can be stored can be across unique systems before overflowing occurs giving us an undesired result? and is mainly used for portability?
@std::size_t doesn't return anything, it's a type, like int, float, etc.
It can store the number that describes the size of the largest type.
Let's say your compiler allows an object to be 4294967295 bytes (maximum on a 32bit system) in size.
@std::size_t can store that number.
This is unrelated to integers. You can check how large an integer is using @sizeof
which probably print 4.
@std::size_t is required, because you need a type that can store sizes. Looking at @sizeof, you need a type that can store the return value. This is @std::size_t.
Okay, i got that one really wrong.
So i think i understand it better. size_t is an integer data type that can store the largest object in size(in bytes) that the compiler will allow. It's useful as a return value for sizeof because it's unsigned(memory can only be positive integer value) and can work with the largest objects allowed so it's guaranteed to safely return a value of all data types. Plus it is also useful for array indexing and for loops in place of int as it can store the largest value in bytes so there is less chance of overflow?
Is this correct? Is there a way to check for integer overflow just in case or is that not really needed if using size_t?
Right.
Be careful when using it as an iterator in loops. It's less likely to overflow, but if you don't watch out, it might underflow (go below 0).
> Is there a way to check for integer overflow just in case or is that not really needed if using size_t?
If you're working close to min/max values, you should verify that the over/underflow won't occur _before_ doing the calculation.
Hi,
I have a question on size_t. I am sure my system is 64 bits, I expected to see std::cout << sizeof(size_t); print out 8 on console.
But when I ran above line in Eclipse, the output is 8, the output from code blocks is 4, what I am doing wrong here?
Thanks!
On a 64 bit system, you can compile in 32 or 64 bit. If you're using g++ or clang++, the flag you're looking for is -m64
If you're using something else or don't know how to set compiler flags, you'll have to search through code::block's settings.
Thank you for the reply.
Yes, if I run g++ with -m64 from command line. it works OK.
In CodeBlocks, I set /Build options/Compiler settings/Compiler Flags/"Target x_86_64(64bit) [-m64]" to true.
It turn out compile error - "sorry, unimplemented: 64-bit mode not compiled in".
Are there other settings need to turn on? Looks like CodeBlocks issue.
It looks like your Code::Blocks is using a different compiler or version. If it works on the command line, it should work in your IDE (Unless the compiler is different).
I don't have Code::Blocks so I won't be of much help from here on.
I understand that using and unsigned short and going from it's max limit of 65,535 (1111 1111 1111 1111) to 65,536 (1 0000 0000 0000 000) results in only being able to keep the right most 16 bits ( 1 [0000 0000 0000 0000] ) subsequently returning a 0.
But how do we end up with the binary value of 1111 1111 1111 1111 (65,535) from the binary value of 0 if we have and unsigned short assigned as -1?
Why does it “wrap around” the top of the range like that?
Hi Jeremy!
Lesson 3.7 covers binary representation of integers (You're looking for two's complement).
Thanks. I finally made it to 3.7
So if i understand correctly, it's not that it wraps around, it's just -1 is signed and represented as 1111 1111 1111 1111 in binary when using two's complement.
So from the overflow example above:
Is that technically considered overflow since the compiler is just storing -1 (1111 1111 1111 1111) into memory? It's only since the data type x in the example is declared unsigned that the compiler interprets the stored binary value of 1111 1111 1111 1111 as 65,535?
EDIT** I get why the example is considered overflow now, it's that we overflowed/stepped out of our range when we went below 0 decrementing by 1. It's not that we directly assigned a negative number to an unsigned data type/
Two's complement is really making my brain melt. But i think i am starting to get it.
After editing a comment, the syntax highlighter will work after refreshing the page.
My reply to your original comment is https://www.learncpp.com/cpp-tutorial/24-integers/comment-page-3/#comment-391303
Hi, dear teacher!
Please correct
"per the table above, 4,294,967,295 bytes" - it's not bytes
Thanks for great site!
Thanks for pointing out the typo!
Hello,
I just have one quick question:
Why would we use long if the normal int is as well 4 bytes? Is it because the size of int is unsure? If so should we then stop using int, or is int fine for the learning part of C++?
Thanks!
*edit: With the next chapter I see that the long double can be(and is with my labtop) as long as the double. So am I right to assume that there is more difference between the variable?
again Thanks!
> normal int
There is no such thing as a "normal" int. The size of an int is decided by your compiler. "long" suggests that the compiler should use a larger type, but it doesn't have to.
Your compiler might use the same type for long int/int and long double/double, another compiler or other compiler settings might behave differently.
OK, thank you!
Hi
I am wondering about the value for 1 byte that it should be -127 instead of 128.
1 byte signed -128 to 127
I think it should be from -127 to +127.
-111 1111 TO +111 1111
-127 TO 127
can you please show how you get these ranges and tell me which bit is used for sign ?
Thanks
-127 to 127 (inclusive) means that there are 255 possible numbers. There must be 2^8 = 256 numbers, making your statement impossible.
0000'0000 to 0111'1111 (inclusive) are used to represent non-negative numbers (0 to 127 (inclusive))
1000'0000 to 1111'1111 (inclusive) are used to represent negative numbers (-1 to -128 (inclusive))
Lesson 3.7 goes into more detail about binary representations of integers.
Since I'm coding a 32bit Console application, that means size_t = 4, as you also demonstrated in this lesson.
Shouldn't that then mean long long integers (being 64bit) can't be used in such an application?
I tested it however, and it seems like they can...
On further investigation, interestingly:
... compiles and runs fine. However, if I go over this number (which, as far as I understand it, should be half of the range available to this variable):
... I get the following compile error:
error: integer constant is so large that it is unsigned
unsigned long long x { 9223372036854775808 };
^
Is this because of the 32bit limitation, and size_t being 4?
Using an implicitly 'signed' long long seems to work fine though:
... and ...
... both compile without issue.
This, to me, suggests that the full 8 byte range of long long is available to this 32bit console application, but only half the range of the 8 byte 'unsigned' long long is available (4 bytes worth).
Is it because the 'negative version' of the number can be stored in 4 bytes, just like the positive version, but with the 'two's complement flipping' of the bit values? I suppose that would explain it, because going over this number (either positive or negative) would then need that extra bit which is unavailable in this 32bit application...
I'd really appreciate an explanation of what's going on here!
Cheers :)
Hi!
> Shouldn't that then mean long long integers (being 64bit) can't be used in such an application?
No. You can use variables as big as you like, but using 64bit variables in a 32bit application is slower than using using 64bit variables in a 64bit application or using 32bit variables in a 32bit application.
> integer constant is so large that it is unsigned
> Is this because of the 32bit limitation, and size_t being 4?
No. @x is an unsigned long long, but the number you're initializing @x with is a long, or at least that's what it's supposed to be, but it can't, because it's too large.
C++ determines the type of the value before it considers the type of the variable you're initializing with said value.
You need to explicitly make the number an unsigned long long by appending "ull"
This is covered in lesson 2.8
Hi nascardriver,
Many thanks for your reply! I've tested the 'ull' appendage and that works as you suggest.
I'm still a little confused though, since the lesson material seems to suggest that 8 byte objects shouldn't be usable when size_t is 4:
"size_t is an unsigned, integral value that is typically used to represent the size or length of objects, and it is defined to be big enough to hold the size of the largest object createable on your system."
and
"By definition, any object larger than the largest value size_t can hold is considered ill-formed (and will cause a compile error), as the sizeof operator would not be able to return the size without overflow."
Isn't using a long long, of size 8 bytes, considered 'ill-formed' (as per the above statement) in a 32-bit application where size_t = 4, and therefore shouldn't I be getting a compile error as the statement suggests?
That's why I wanted to test it in the first place, and being able to use long long integers made me want to investigate further...
Cheers!
I see why you're confused as the quoted text can be understood two ways. What it means to say is that the size of any object in bytes cannot exceed the maximum value @std::size_t can store.
Let's the maximum value of @std::size_t is (2^64)-1 = 18446744073709551615, which is the case in 64bit applications. Then the maximum size of an object is (2^64)-1 bytes = 16EiB.
Thanks for the clarification! I think I see now...
So, in a 64-bit application the maximum size of a single object is over 18 billion Gigabytes?
Can one even make an object that large? To me that seems to be a limit with no practical application or meaning, at least from my current (very possibly naive) position...
> size of a single object is over 18 billion Gigabytes?
Yes
> Can one even make an object that large?
If you have enough and fast memory, sure.
> that seems to be a limit with no practical application or meaning
It doesn't mean that you're supposed to create objects that large.
64 bit memory can address (2^64)-1 bytes. We're far from reaching that limit, but theoretically a 64 bit system could have that much memory, meaning that it'd be possible to create an object that large (assuming nothing else is taking up memory). When an object of that size can be created, there needs to be a variable that can store the size of that object. Hello @std::size_t.
Good feedback. I've updated the lesson text to try and clarify this better. Let me know if it's still unclear.
Thanks Alex, looks less ambiguous now!
Hi,
So, there are no any compiler-errors / warnings when an int overflow occurs, just undesired values, right?
I did find some information on methods concerning detecting / testing for overflows though, what do you think about those in general? How common are such techniques?
Thanks! :)
As long as you're using the proper types and clamping the values that should be clamped, integers overflow aren't a problem.
Ok, Thanks! :)
"size_t is guaranteed to be unsigned and at least 16 bits, but on most systems will be equivalent to the largest unsigned integer that your architecture supports. For 32-bit applications, size_t will typically be a 32 bit unsigned integer, and for a 64-bit application, size_t will typically be a 64-bit unsigned integer."
Largest unsigned integer on 32-bit platform is 64 bits(long long), but the size of size_t is 32 bits?
Good catch. I've updated the lesson to remove the reference to the largest unsigned integer your architecture supports, as the introduction of long long makes this untrue on many 32-bit systems.
Hi Aakash!
16 bytes would be huge, it should be 16 bits (or 2 bytes). What worries me is the 16, I can't find any source stating this number.
My understanding is that the 16 bit minimum was defined in the C definition and inherited by C++.
"Much like an integer can vary in size depending on the system, size_t also varies in size. size_t is guaranteed to be unsigned and at least 16 bites,"
at end, it must be "bytes" resulting in
"Much like an integer can vary in size depending on the system, size_t also varies in size. size_t is guaranteed to be unsigned and at least 16 bytes,"
Typo fixed. This is why I am not a professional editor. :) Thanks!