An integer type variable is a variable that can only hold whole numbers (eg. -2, -1, 0, 1, 2). C++ actually has four different integer variables available for use: char, short, int, and long. The only difference between these different integer types is that they have varying sizes — the larger integers can hold bigger numbers. You can use the sizeof operator to determine how large each type is on your machine.
In the following tutorials, we will typically assume:
- a char is 1 byte
- a short is 2 bytes
- an int is either 2 or 4 bytes
- a long is 4 bytes
Declaring some integers:
char chChar; short int nShort; // "short int" is technically correct short nShort2; // "short" is preferred shorthand int nInteger; long int nLong; // "long int" is technically correct long nLong2; // "long" is preferred shorthand
While short int and long int are technically correct, we prefer to use the shorthand versions short and long instead. Adding the prefix int makes the type harder to distinguish from variables of type int. This can lead to mistakes (such as overflow) if the short or long modifier is inadvertently missed.
Because the size of char, short, int, and long can vary depending on the compiler and/or computer architecture, it can be instructive to refer to integers by their size rather than name. We often refer to integers by the number of bits or bytes a variable of that type is allocated.
As you learned in the last section, a variable with n bits can store 2^n different values. We call the set of values that a data type can hold it’s range. Integers can have two different ranges, depending on whether they are signed or unsigned.
Signed and unsigned variables
A signed integer is a variable that can hold both negative and positive numbers. To declare a variable as signed, you can use the signed keyword:
signed char chChar; signed short nShort; signed int nInt; signed long nLong;
A 1-byte signed variable has a range of -128 to 127. Any value between -128 and 127 (inclusive) can be put in a 1-byte signed variable safely.
Sometimes, we know in advance that we are not going to need negative numbers. This is common when using a variable to store the quantity or size of something (such as your height — it doesn’t make sense to have a negative height!). An unsigned integer is one that can only hold positive values. To declare a variable as unsigned, use the unsigned keyword:
unsigned char chChar; unsigned short nShort; unsigned int nInt; unsigned long nLong;
A 1-byte unsigned variable has a range of 0 to 255.
Note that declaring a variable as unsigned means that it can not store negative numbers, but it can store positive numbers that are twice as large!
So what happens if we do not declare a variable as signed or unsigned? All integer variables except char are signed by default. Char can be either signed or unsigned by default (but is usually signed).
short nShort; // signed by default int nInt; // signed by default long nLong; // signed by default char chChar; // can be signed or unsigned by default, but probably signed.
New programmers sometimes get signed and unsigned mixed up. The following is a simple way to remember the difference: in order to differentiate positive and negative numbers, we typically use a negative sign. If a sign is not provided, we assume a number is positive. Consequently, an integer with a sign (a signed integer) can tell the difference between positive and negative. An integer without a sign (an unsigned integer) assumes all values are positive.
Now that you understand the difference between signed and unsigned, let’s take a look at the ranges for different sized signed and unsigned variables:
| Size/Type | Range |
|---|---|
| 1 byte signed | -128 to 127 |
| 1 byte unsigned | 0 to 255 |
| 2 byte signed | -32,768 to 32,767 |
| 2 byte unsigned | 0 to 65,535 |
| 4 byte signed | -2,147,483,648 to 2,147,483,647 |
| 4 byte unsigned | 0 to 4,294,967,296 |
| 8 byte signed | -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807 |
| 8 byte unsigned | 0 to 18,446,744,073,709,551,615 |
For the math inclined, an n-bit signed variable has a range of -(2^(n-1)) to (2^(n-1))-1. An n-bit unsigned variable has a range of 0 to (2^n)-1. For the non-math inclined… use the table. :)
What happens if we try to put a number outside of the data type’s range into our variable? We get…
Overflow
In binary, we count from 0 to 15 like this: 0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111. As you can see, the larger numbers require more bits to represent. Because our variables have a fixed number of bits, this puts a limit on the largest number they can hold.
Consider a hypothetical variable that can only hold 4 bits. Any of the binary numbers enumerated above would fit comfortably inside this variable because none of them are larger than 4 bits. But what happens if we try to assign a value that takes 5 bits to our variable? We get overflow: our variable will only store 4 of the 5 bits, and the excess bits are lost.
Overflow occurs when bits are lost because a variable does not have enough memory to store them.
We can see this in action with the following program:
#include <iostream>
int main()
{
using namespace std;
unsigned short x = 65535; // largest 2-byte unsigned value possible
cout << "x was: " << x << endl;
x = x + 1; // We desire 65536, but we get overflow!
cout << "x is now: " << x << endl;
}
What do you think the result of this program will be?
x was: 65535 x is now: 0
What happened? Informally, we overflowed the variable by trying to put a number that was too big into it, and the result is that our value “wrapped around” back to the beginning of the range. For non-integer data types, overflowed variables do not always wrap around the range, so do not rely on this happening!
The following paragraph explains exactly why we ended up getting a value of 0 after overflow. It is optional reading. If all this binary stuff is confusing, you can skip it.
The number 65,535 is represented by the bit pattern 1111 1111 1111 1111 in binary. 65,535 is the largest number an unsigned 2 byte (16-bit) integer can hold, as it uses all 16 bits. When we add 1 to the value, the new value should be 65,536. However, the bit pattern of 65,536 is represented in binary as 1 0000 0000 0000 0000, which is 17 bits! Consequently, the highest bit (which is the 1) is lost, and the low 16 bits are all that is left. The bit pattern 0000 0000 0000 0000 corresponds to the number 0, which is our result!
Similarly, we can overflow the bottom end of our range as well.
#include <iostream>
int main()
{
using namespace std;
unsigned short x = 0; // smallest 2-byte unsigned value possible
cout << "x was: " << x << endl;
x = x - 1; // We expect -1, we get overflow!
cout << "x is now: " << x << endl;
}
x was: 0 x is now: 65535
In the case of a signed integer, the result is identical.
#include <iostream>
int main()
{
using namespace std;
signed short x = 32767; // largest 2-byte signed value possible
cout << "x was: " << x << endl;
x = x + 1; // We desire 32768, but we get overflow!
cout << "x is now: " << x << endl;
}
x was: 32767 x is now: -32768
Overflow results in information being lost, which is almost never desirable. If there is ANY doubt that a variable might need to store a value that falls outside it’s range, use a larger variable!
Integer division
Integer division can also cause issues because dividing 2 integers can produce a fractional result, and integers can not store fractions. Consider the statement int x = 5 / 3;. Under normal mathematical rules, x would be assigned the value of 5/3, which is 1.6666. However, in integer division, the fraction is dropped, so x is assigned the value of 1. Integer division always drops the fraction — it does not round.
Fixed width integers
Some compilers provide access to fixed width integers (integers whose size is not dependent on the platform). Since these are not officially part of the C++ standard, information on these has been relegated to appendix A.6 — Fixed width integers. Nevertheless, I recommend you check them out.
2.5 — Floating point numbers
|
Index
|
2.3 — Variable sizes and the sizeof operator
|
2.5 — Floating point numbers
Index
2.3 — Variable sizes and the sizeof operator
Would you say it is a good idea to work with signed integers even if it is unlikely the number would be negative, just as a precaution? Or maybe allow it but have a warning print to the screen?
That is actually a really difficult question. :) I have seen various programmers argue it either way, and there’s no clear answer.
Bjarne Stroustrup (who designed C++) says, “The unsigned integer types are ideal for uses that treat storage as a bit array. Using an unsigned instead of an int to gain one more bit to represent positive integers is almost never a good idea. Attempts to ensure that some values are positive by declaring variables unsigned will typically be defeated by the implicit conversion rules.”
I think Bjarne is correct on this one.
Using signed instead of unsigned integers even when you don’t expect negative numbers gives you a few benefits:
1) Many programmers use signed integers even when only dealing with positive numbers, because negative numbers can then be used as “error conditions”. For example, it’s pretty common to write a function that is expected to return a positive number. However, you can have it return a negative number if something goes wrong. That way, the caller has a way of detecting something went astray. (Note: You can also use exception handling as an alternative mechanism for returning errors)
2) What happens in this case:
int foo(unsigned int nValue)
{
// something
}
caller:
int nValue = foo(-1);
The -1 gets silently converted into an unsigned integer (which would be a large positive number), and the function has no way of detecting that an invalid input was given to it.
3) If you expect a number to be positive, and your signed variable suddenly has a negative value, that’s a good indication your algorithm is wrong.
In short, as a rule of thumb, unless you have a good reason not to, it is better to use signed integers.
Fun fact: The old Final Fantasy games on the NES only allowed your stats to go up to 255 because they used a 1 byte unsigned variable to store stats. It would be neat to see someone allude this in a modern game… especially when memory isn’t a huge issue anymore. :)
Memory is ALWAYS a huge issue. Modern games usually have more stats or more monsters to keep track of, so doubling or quadrupling the memory needs, just because you can, is never a good idea. Designing good rules usually allows to reduce the height of stats, rather than extend it.
Another great set of examples, thanks.
Alex, I see that you used
iostream.hin this page’s examples instead ofiostream. Is this intentional? I thought you said earlier thatiostream.hdid not support namespaces.That’s just a mistake on my part. You are correct. I’ll fix up the examples.
Hi Alex,
First of all thank you very much for such a great tutorial.
I have a small question, I am unable to an value more than 2,147,483,647 if though I am declaring the variable as double, long double, unsigned int. I dont understand why, I have just started learning C++ and I find your tutorial very informative.
Thanks
Lal
#include
int main()
{
long double nshort;
nshort=2147483648;
cout << “short:” << sizeof(nshort) << ” bytes” << endl;
cout <<”\n” << nshort;
}
You are encountering this behavior because 2147483647 is the maximum signed long value. If you need a larger number, you have a couple of options:
1) Use an unsigned long
2) Use a double (covered in the next lesson)
Whenever you’re working with floating point values (floats and doubles and long doubles), make sure you add a .0 suffix if your value is a whole number:
double dValue = 2147483648.0;
Thanks Alex,
Double works but unsigned long does not, i get this error for unsigned long
warning: decimal integer constant is so large that it is unsigned
I forgot to mention: if you use an unsigned long, you have to use the U suffix for the literal:
long nMyNumber = 2147483648U;
This tells the compiler to treat the literal as an unsigned number instead of a signed one.
[...] 2007 Prev/Next Posts « 2.4 — Integers | Home | Break Time — Dice Wars » Saturday, June 9th, 2007 at 12:07 [...]
[...] 2.4 — Integers [...]
I was just running the formulas for calculating the ranges of unsigned variables, and noticed that the table and formula don’t seem to match. I am not a real math person, so I may be misreading the formula, but shouldn’t an n-bit unsigned variable have a range of 0 to 2(2^n-1.)-1?
-Alex
When counting from 0 to 15 in binary, you have written 0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1011, 1101, 1111. It seems that you have left out a few numbers. You seem to have have skipped the numbers 12(1100) and 14(1110).
As far as I can see “unasigned” is still black in the examples on the site (and not colored like “signed”).
Yeah, I guess the syntax highlighting module being used doesn’t recognize that as a keyword. I’m not sure why.
I have a question- if long and int are both the same amount of bytes, do they hold the same amount?
Yes.
When overflow, does it dangerous? It can change other memory bits right (That may be used by other variables/application)?
OK, I understand after reading forward.
Dangerouse because it could change the other variables.
Thank you
Actually, overflow will just result in the most significant bits being lost. It won’t overflow into other variables.
Actually, overflow will just result in the most significant bits being lost. It won’t overflow into other variables.
It’s just because mathematical operations do not work with memory directly. The operand is put into CPU register (mostly EAX (on x86 machines) or its part – as the only register for integer mathematical purposes) for processing. The result (which can also occupy EDX register) is then taken from the initial place (EAX register) leading to higher bits lose.
However, if you’re dealing with putting the contents of the EAX register back into memory and the memory isn’t large enough to hold the register’s value (putting it into a char variable) that might cause problems.
Back to the original “is it dangerous?” if your plane altimeter value overflows and the auto-pilot now thinks you’re at 0 above ground and says CLIMB – NOW when you’re really way up in the air, who knows what could happen? Dangerous all depends on the application.
Alot of computer exploits occur when something is overlowed or underflowed and the OS switches into protected modes for recovery, and next thing you know your PC is compromised and sending out spam to thousands of people… or you get a blue screen of death or an eternal spinny (roulette) wheel of death.
However, if you’re dealing with putting the contents of the EAX register back into memory and the memory isn’t large enough to hold the register’s value (putting it into a char variable) that might cause problems.
Back to the original “is it dangerous?” if your plane altimeter value overflows and the auto-pilot now thinks you’re at 0 above ground and says CLIMB – NOW when you’re really way up in the air, who knows what could happen? Dangerous all depends on the application.
A lot of computer exploits occur when something is overlowed or underflowed and the OS switches into protected modes for recovery, and next thing you know your PC is compromised and sending out spam to thousands of people… or you get a blue screen of death or an eternal spinny (roulette) wheel of death.
Thanks for your tutorial,
now i’m able to make my own String, Array, etc classes similar to std::string and vector after 4 months learning c++
Thank you very much.
Great tutorial! Knew nothing this morning, now already something.
Detail: the math behind the table with unsigned/signed range:
doesn’t the n-bit unsigned variable have a range of 0 to 2^n instead of 0 to 2^n-1?
As it was mentioned above:
As you learned in the last section, a variable with n bits can store 2^n different values…
As 0 (zero) is also a value, the maximum number is (2^n)-1 and range becomes 0 … (2^n)-1 (inclusive).
What do you do if 4/8/16 bits isn’t big enough? For instance, number theorists like to do arithmetic on very big numbers, ~ 100 to 200 digits large.
On modern architectures, generally longs are 32 bits. Most modern compilers also give you access to a 64-bit integer type (often called a long long, but sometimes it has other names, like __int64).
However, if you need even larger integers, then you will have to write your own data type. You will learn how to do this in the section on classes (chapter 8).
I have a few questions:
1. Would these two programs ALWAYS output the same thing? (code below)
2. If yes, would Prog1 be faster than Prog2?
3. Is there a chance something can go wrong? (Okay, I know there’s always a chance, I’m asking if it’s significant.)
Ex. different variable sizes on different computers, although integers should have constant sizes. In 2.3 — Variable sizes and the sizeof operator, the sizeof() test program returned 8B for long double on your computer while I got 12B but I can’t think of a way to use this efficiently for non-integer types so it doesn’t matter.
Yes, I know you’ll say I shouldn’t write such obscure code, this is just theoretically (besides, how often could I use such a function?).
Prog1:
#include <iostream> int main() { unsigned short num(12345); num += 65536; std::cout << num; return 0; }Prog2:
#include <iostream> int main() { unsigned short num(12345); num = (num + 65536) % 65536; std::cout << num; return 0; }You can replace 65536 with a multiple. I tried 214743648 and it worked fine.
I believe you should post this in the forum, it doesnt really belong here.
Keep coding. Use it for good. :)
How come you don’t need to have “return 0;” in these example programs?
whatis d difrence betn c++ and vc++ ???? is it just tht vc++ is writn in visual studio??? what about turbo c++??
Thanks again for your excellent tutorials Alex.
Just 2 quests;
1. in the ‘Range’ table above, 4 byte unsigned 0 to 4,294,967,296: is it 4,294,967,296 or 4,294,967,295?
2. when Stroustrup says “The unsigned integer types are ideal for uses that treat storage as a bit array.”, does he mean when you are using the bits within a variable to check if they are on or off?
How do you prevent an integer overflow?
My program prevents users entering a number higher than a billion, that works just fine. But, however, if a user enters a number that exceeds the integer-range, my program gets stuck in a loop. Is there a way to prevent a user from entering a number that causes an overflow?
65535 is 0011011000110101001101010011001100110101
65536 is 0011011000110101001101010011001100110110
how is short able to maintain the first but not the second?
can u please explain?
I don’t think you have your binary right,
Adam,
In the overflow section, I noticed you only have 14 numbers. Shouldn’t there be 16 since you are starting from 0. Wouldn’t it be:
0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1011, 1101, 1111
0 – 0
1 – 1
2 – 10
3 – 11
4 – 100
5 – 101
6 – 110
7 – 111
8 -1000
9 -1001
10-1010
11-1011
12-1101
13-1110
14-1111
15-10000
?
I thing it is 0-1-10-11-100-101-110-111-1000-1001-1010-1011-1100-1101-1110 to make a 15 and to make a 16, just add a 1111……
Let me know if I am mistaken…..great tutorials…
Concrete basics of binary counting:
If you understand that in decimal system 248 stands for
2 x 10^2 +
4 x 10^1 +
8 x 10^0
and 13 for
1 x 10^1 +
3 x 10^0
then it is quite easy to convert in your head small decimals to binary and vice versa. For example 1011 is
1 x 2^3 = 8 +
0 x 2^2 = 0 +
1 x 2^1 = 2 +
1 x 2^1 = 1
= 11.
When you go one integer (1) up in whatever the base (binary, octal, decimal etc.) you increase the lowest nominator to the highest possible until it reaches maximum, then you increase the second to the lowest if possible, if not, the third to the lowest etc. For example in octal system next from 4677 is 4700, because you can’t get higher than 7 (and after that of course 4701). In binary next from 1011 is 1100 (because from right to left first two 1′s can’t get higher). After 1100 -> 1101, 1110, 1111. And you can assure it by counting:
1100 = 8 + 4 + 0 + 0 = 12
1101 = 8 + 4 + 0 + 1 = 13
1110 = 8 + 4 + 2 + 0 = 14
1111 = 8 + 4 + 2 + 1 = 15.
Yes, fixed. :) Thanks for pointing that out.
oops…
12 – 1100
13 – 1101
14 – 1110
15 – 1111
Can someone tell me why this does not work.
As per the above lesson char is type of INT. so this should work.
For “1 byte signed” the range is -128 to 127, how is -128 represented in 1 byte, does’nt it cause an overflow?
127 => 01111111
-128 => 111111111 (the right most 1 represents – negative)
Also why cant we represent 128 in 1 byte (why is the range only till 127?)
128 => 10000000
Thx
What is the difference between the “long” and “int” variable types? (They both have the same size).
when you say, an int variable has a size of 2 bytes or 4 bytes, what do u mean? does it dynamically change size from 2 to 4 bytes as the number get larger?
Hi Alex
Did you realise you were missing the number 12 and 14 from your binary example?
You have:
0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1011, 1101, 1111
Should be:
0, 1, 10, 11, 100, 101, 110, 111, 1000, 1001, 1010, 1011, 1100, 1101, 1110, 1111
Don’t know if that was intentional but it certainly hurt by brain (binary newbie) figuring it out!
Cheers for the awesome tutorial, I’m so grateful for your time :)
Kevin
Why in these tutorials instead of displaying the actual code i get characters
<---- in binary."
[...] you recall from lesson 2.4 — Integers, the largest integer type C++03 defines is “long”. Long has a platform-specific size [...]
8 byte signed -9,223,372,036,854,775,807 to 9,223,372,036,854,775,807
should it not be
8 byte signed -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
i can understand how integer overflow happens when you increase an unsigned integer -
i.e. 65535 = 1111 1111 1111 1111
and 65536 = [1] 0000 0000 0000 0000
so only given 2 bytes of data this reverts back to meaning 0.
However, I can’t understand how this happens in reverse?
i.e. 0 = 0000 0000 0000 0000
So when you subtract one to get -1, how does this revert back to 1111 1111 1111 111
in terms of how memory is stored?
* missed the last 1 off the 2nd to last line
[...] really fun to read a “basic” article like this one about Integers, and see so many people interact with their own questions, examples and thoughts about the topic. [...]
Thanks for the amazing tutorial Alex.
I must point out what has already been mentioned several times, as I believe that it can be very misleading to the new student. Unless it was intentional, it appears as if the binary counting in this tutorial differs from every other binary resource that I have read, as well as my own personal understanding of how binary addition works.
It seems as if the last 2 digits in the sequence mentioned above (1100 and 1110) are actually 12 and 14 and should be replaced with a sequence of four digits:
12, 13, 14, and 15
1100, 1101, 1110, 1111
Thanks Again!
Sorry, the edit timer ran out and I could not fix my mistake. I meant to say that the last 2 digits in the sequence mentioned above (1101 and 1111) are actually 13 and 15 (12 and 14 are the ones that are missing) and should be replaced with a sequence of four digits:
12, 13, 14, and 15
1100, 1101, 1110, 1111
Yes, it’s been fixed now. Thanks for the notice!