4.5 — Unsigned integers, and why to avoid them – Learn C++ (2023)

Unsigned integers

In the previous lesson (4.4 -- Signed integers), we covered signed integers, which are a set of types that can hold positive and negative whole numbers, including 0.

C++ also supports unsigned integers. Unsigned integers are integers that can only hold non-negative whole numbers.

Defining unsigned integers

To define an unsigned integer, we use the unsigned keyword. By convention, this is placed before the type:

unsigned short us;unsigned int ui;unsigned long ul;unsigned long long ull;

Unsigned integer range

A 1-byte unsigned integer has a range of 0 to 255. Compare this to the 1-byte signed integer range of -128 to 127. Both can store 256 different values, but signed integers use half of their range for negative numbers, whereas unsigned integers can store positive numbers that are twice as large.

Here’s a table showing the range for unsigned integers:

Size/TypeRange
1 byte unsigned0 to 255
2 byte unsigned0 to 65,535
4 byte unsigned0 to 4,294,967,295
8 byte unsigned0 to 18,446,744,073,709,551,615

An n-bit unsigned variable has a range of 0 to (2n)-1.

When no negative numbers are required, unsigned integers are well-suited for networking and systems with little memory, because unsigned integers can store more positive numbers without taking up extra memory.

Remembering the terms signed and unsigned

(Video) C++ Weekly - Ep 284 - C++20's Safe Integer Comparisons

New programmers sometimes get signed and unsigned mixed up. The following is a simple way to remember the difference: in order to differentiate negative numbers from positive ones, we 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.

Unsigned integer overflow

What happens if we try to store the number 280 (which requires 9 bits to represent) in a 1-byte (8-bit) unsigned integer? The answer is overflow.

Author’s note

Oddly, the C++ standard explicitly says “a computation involving unsigned operands can never overflow”. This is contrary to general programming consensus that integer overflow encompasses both signed and unsigned use cases (cite). Given that most programmers would consider this overflow, we’ll call this overflow despite C++’s statements to the contrary.

If an unsigned value is out of range, it is divided by one greater than the largest number of the type, and only the remainder kept.

The number 280 is too big to fit in our 1-byte range of 0 to 255. 1 greater than the largest number of the type is 256. Therefore, we divide 280 by 256, getting 1 remainder 24. The remainder of 24 is what is stored.

Here’s another way to think about the same thing. Any number bigger than the largest number representable by the type simply “wraps around” (sometimes called “modulo wrapping”). 255 is in range of a 1-byte integer, so 255 is fine. 256, however, is outside the range, so it wraps around to the value 0. 257 wraps around to the value 1. 280 wraps around to the value 24.

Let’s take a look at this using 2-byte shorts:

#include <iostream>int main(){ unsigned short x{ 65535 }; // largest 16-bit unsigned value possible std::cout << "x was: " << x << '\n'; x = 65536; // 65536 is out of our range, so we get wrap-around std::cout << "x is now: " << x << '\n'; x = 65537; // 65537 is out of our range, so we get wrap-around std::cout << "x is now: " << x << '\n'; return 0;}

What do you think the result of this program will be?

(Note: If you try to compile the above program, your compiler should issue warnings about overflow or truncation -- you’ll need to disable “treat warnings as errors” to run the program)

(Video) C++ Tutorial for Beginners - Full Course

x was: 65535x is now: 0x is now: 1

It’s possible to wrap around the other direction as well. 0 is representable in a 2-byte unsigned integer, so that’s fine. -1 is not representable, so it wraps around to the top of the range, producing the value 65535. -2 wraps around to 65534. And so forth.

#include <iostream>int main(){ unsigned short x{ 0 }; // smallest 2-byte unsigned value possible std::cout << "x was: " << x << '\n'; x = -1; // -1 is out of our range, so we get wrap-around std::cout << "x is now: " << x << '\n'; x = -2; // -2 is out of our range, so we get wrap-around std::cout << "x is now: " << x << '\n'; return 0;}
x was: 0x is now: 65535x is now: 65534

The above code triggers a warning in some compilers, because the compiler detects that the integer literal is out-of-range for the given type. If you want to compile the code anyway, temporarily disable “Treat warnings as errors”.

As an aside…

Many notable bugs in video game history happened due to wrap around behavior with unsigned integers. In the arcade game Donkey Kong, it’s not possible to go past level 22 due to an overflow bug that leaves the user with not enough bonus time to complete the level.

In the PC game Civilization, Gandhi was known for often being the first one to use nuclear weapons, which seems contrary to his expected passive nature. Players had a theory that Gandhi’s aggression setting was initially set at 1, but if he chose a democratic government, he’d get a -2 aggression modifier (lowering his current aggression value by 2). This would cause his aggression to overflow to 255, making him maximally aggressive! However, more recently Sid Meier (the game’s author) clarified that this wasn’t actually the case.

The controversy over unsigned numbers

Many developers (and some large development houses, such as Google) believe that developers should generally avoid unsigned integers.

This is largely because of two behaviors that can cause problems.

First, consider the subtraction of two unsigned numbers, such as 3 and 5. 3 minus 5 is -2, but -2 can’t be represented as an unsigned number.

#include <iostream>int main(){unsigned int x{ 3 };unsigned int y{ 5 };std::cout << x - y << '\n';return 0;}

On the author’s machine, this seemingly innocent looking program produces the result:

4294967294

This occurs due to -2 wrapping around to a number close to the top of the range of a 4-byte integer. Another common unwanted wrap-around happens when an unsigned integer is repeatedly decremented by 1 (using the -- operator). You’ll see an example of this when loops are introduced.

(Video) C++ Weekly - Ep 310 - Your Small Integer Operations Are Broken!

Second, unexpected behavior can result when you mix signed and unsigned integers. In a mathematical operation in C++ (e.g. arithmetic or comparison), if one signed and one unsigned integer are used, the signed integer will be converted to unsigned. And because unsigned integers can not store negative numbers, this can result in loss of data.

Consider the following program demonstrating this:

#include <iostream>int main(){ signed int s { -1 }; unsigned int u { 1 }; if (s < u) // -1 is implicitly converted to 4294967295, and 4294967295 < 1 is false std::cout << "-1 is less than 1\n"; else std::cout << "1 is less than -1\n"; // this statement executes return 0;}

This program is well formed, compiles, and is logically consistent to the eye. But it prints the wrong answer. And while your compiler should warn you about a signed/unsigned mismatch in this case, your compiler will also generate identical warnings for other cases that do not suffer from this problem (e.g. when both numbers are positive), making it hard to detect when there is an actual problem.

Related content

We cover if-statements in upcoming lesson 4.10 -- Introduction to if statements.

Additionally, there are other problematic cases that are essentially undetectable. Consider the following:

void doSomething(unsigned int x){ // Run some code x times std::cout << "x is " << x << '\n';}int main(){ doSomething(-1); return 0;}

The author of doSomething() was expecting someone to call this function with only positive numbers. But the caller is passing in -1 -- clearly a mistake, but one made none-the-less. What happens in this case?

The signed argument of -1 gets implicitly converted to an unsigned parameter. -1 isn’t in the range of an unsigned number, so it wraps around to some large number (probably 4294967295). Then your program goes ballistic. Worse, there’s no good way to guard against this condition from happening. C++ will freely convert between signed and unsigned numbers, but it won’t do any range checking to make sure you don’t overflow your type.

All of these problems are commonly encountered, produce unexpected behavior, and are hard to find, even using automated tools designed to detect problem cases.

Given the above, the somewhat controversial best practice that we’ll advocate for is to avoid unsigned types except in specific circumstances.

Best practice

Favor signed numbers over unsigned numbers for holding quantities (even quantities that should be non-negative) and mathematical operations. Avoid mixing signed and unsigned numbers.

(Video) Serial Communication Tutorial: Negative Numbers and other Data Types (Two's Complement)

Related content

Additional material in support of the above recommendations (also covers refutation of some common counter-arguments):

  1. Interactive C++ panel (see 12:12-13:08, 42:40-45:26, and 1:02:50-1:03:15)
  2. Subscripts and sizes should be signed
  3. Unsigned integers from the libtorrent blog

So when should you use unsigned numbers?

There are still a few cases in C++ where it’s okay / necessary to use unsigned numbers.

First, unsigned numbers are preferred when dealing with bit manipulation (covered in chapter O -- that’s a capital ‘o’, not a ‘0’). They are also useful when well-defined wrap-around behavior is required (useful in some algorithms like encryption and random number generation).

Second, use of unsigned numbers is still unavoidable in some cases, mainly those having to do with array indexing. We’ll talk more about this in the lessons on arrays and array indexing. In these cases, the unsigned value can be converted to a signed value.

Related content

We discuss how to convert unsigned values to signed values in lesson 4.12 -- Introduction to type conversion and static_cast.

(Video) int Variable (Integer) - C Programming

Also note that if you’re developing for an embedded system (e.g. an Arduino) or some other processor/memory limited context, use of unsigned numbers is more common and accepted (and in some cases, unavoidable) for performance reasons.

Next lesson4.6Fixed-width integers and size_tBack to table of contentsPrevious lesson4.4Signed integers

FAQs

Why do we avoid unsigned integers? ›

In a mathematical operation in C++ (e.g. arithmetic or comparison), if one signed and one unsigned integer are used, the signed integer will be converted to unsigned. And because unsigned integers can not store negative numbers, this can result in loss of data.

Should I use unsigned int C++? ›

You should use unsigned values whenever you are dealing with bit values, i.e. direct representations of the contents of memory; or when doing manipulations such as bit masking or shifting on data, for example when writing low-level code to read binary file formats such as audio files; or if you happen to be doing work ...

Is it good practice to use unsigned int? ›

Unsigned integers ( size_t , uint32_t , and friends) can be hazardous, as signed-to-unsigned integer conversions can happen without so much as a compiler warning.

Why we use unsigned in C++? ›

The unsigned keyword is a data type specifier, that makes a variable only represent non-negative integer numbers (positive numbers and zero). It can be applied only to the char , short , int and long types.

Should I avoid unsigned int? ›

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. Save this answer.

What does unsigned integer mean in C++? ›

unsigned int in C++ is a data type which holds for non-negative values ranging from 0-255.

Is unsigned faster than signed C++? ›

unsigned leads to the same or better performance than signed . Some examples: Division by a constant which is a power of 2 (see also the answer from FredOverflow) Division by a constant number (for example, my compiler implements division by 13 using 2 asm instructions for unsigned, and 6 instructions for signed)

What is the advantage of unsigned int in C? ›

Unsigned int is a data type that can store the data values from zero to positive numbers whereas signed int can store negative values also. It is usually more preferable than signed int as unsigned int is larger than signed int.

What is the difference between signed and unsigned integers in C++? ›

A signed integer is a 32-bit datum that encodes an integer in the range [-2147483648 to 2147483647]. An unsigned integer is a 32-bit datum that encodes a nonnegative integer in the range [0 to 4294967295]. The signed integer is represented in twos complement notation.

Should I use int or unsigned int? ›

Since we use number with positive and negative integers more often than positive integers only, the type Int is the signed integers. If we want a value without a sign, then we use the type UInt . UInt creates a integer of the same bit size as the device's processor can handle.

What are the benefits of declaring a variable as unsigned int instead of int? ›

  • it allows you to use the right shift >> operator safely on any value, as it can't be negative -- using a right shift on a negative value is undefined.
  • it gives you wrap-around mod 2^n arithmetic. With signed values, the effect of underflow/overflow is undefined.

Are unsigned ints faster? ›

Signed versus unsigned integers

In most cases, there is no difference in speed between using signed and unsigned integers. But there are a few cases where it matters: Division by a constant: Unsigned is faster than signed when you divide an integer with a constant. This also applies to the modulo operator %.

What is the max 32-bit unsigned integer in C++? ›

A 32-bit unsigned integer. It has a minimum value of 0 and a maximum value of 4,294,967,295 (inclusive).

Why do we need signed and unsigned? ›

Variables such as integers can be represent in two ways, i.e., signed and unsigned. Signed numbers use sign flag or can be distinguish between negative values and positive values. Whereas unsigned numbers stored only positive numbers but not negative numbers.

What is the advantage of Uint? ›

The main benefit is that int and uint are the smallest (in terms of bitsize) data types which are safe to use in a Go program in conjunction with common Go data types such as slices and maps. The size of int is independent from the size of a pointer *T . The integer type corresponding to *T is uintptr .

What is an example of an unsigned integer? ›

The simplest numbers that we want to represent in the machine are the unsigned integers. These are whole numbers without a sign, for example, 0, 1, 2, 3, …

Should I use signed or unsigned? ›

The potential to employ negative numbers is the primary difference between a signed and an unsigned number. Unsigned numbers can only have values that are equal to or greater than zero. On the other hand, signed numbers have a more natural range that covers negative and positive numbers.

What is 64 bit unsigned integer in C++? ›

C++ - 64-bit unsigned integer: unsigned long long

64-bit unsigned integer type is used to store only pozitiv whole number. 64-bit unsigned integer and his value range: from 0 to 18446744073709551615.

What is the range of unsigned integer in C++? ›

In this article
Type NameBytesRange of Values
int4-2,147,483,648 to 2,147,483,647
unsigned int40 to 4,294,967,295
__int81-128 to 127
unsigned __int810 to 255
21 more rows
Aug 2, 2021

What is 16 bit unsigned integer in C++? ›

C++ - 16-bit unsigned integer: unsigned short

16-bit unsigned integer type is used to store only pozitiv whole number. 16-bit unsigned integer and his value range: from 0 to 65535.

Which is the fastest way in C++? ›

Generally, buffered input will be the fastest. The less frequently you have to flush your input buffer, the faster the input will be.

What are the fastest data types C++? ›

The types int and unsigned int are the most efficient, after they have been loaded in processor registers. Though, with some processor families, they could not be the most efficient type to access in memory.

Is unsigned the same as unsigned int C++? ›

There is no difference. unsigned and unsigned int are both synonyms for the same type (the unsigned version of the int type).

What is the limitation of using unsigned integers? ›

A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.

What is the purpose of unsigned char? ›

The basic ASCII values are in range 0 to 127. The rest part of the ASCII is known as extended ASCII. Using char or signed char we cannot store the extended ASCII values. By using the unsigned char, we can store the extended part as its range is 0 to 255.

Why use uint32_t instead of unsigned int? ›

uint32_t , instead, is used when you need an exact-width integer, e.g. to serialize to file, or when you require that exact range or you rely on unsigned overflow to happen exactly at 2^32-1 . For example, on a 16 bit-processor unsigned int will typically be 16 bits wide, while uint32_t will have to be 32 bits wide.

What is the difference between signed and unsigned data types as used in C? ›

signed and unsigned

signed - allows for storage of both positive and negative numbers. unsigned - allows for storage of only positive numbers.

How do you convert signed int to unsigned int in C++? ›

To convert a signed integer to an unsigned integer, or to convert an unsigned integer to a signed integer you need only use a cast. For example: int a = 6; unsigned int b; int c; b = (unsigned int)a; c = (int)b; Actually in many cases you can dispense with the cast.

What happens if you assign a negative number to an unsigned int? ›

You simply cannot assign a negative value to an object of an unsigned type. Any such value will be converted to the unsigned type before it's assigned, and the result will always be >= 0.

How should I decide which integer type to use? ›

Q: How should I decide which integer type to use? A: If you might need large values (above 32,767 or below -32,767), use long. Otherwise, if space is very important (i.e. if there are large arrays or many structures), use short. Otherwise, use int.

When should I use int C++? ›

C++ int. The int keyword is used to indicate integers. Its size is usually 4 bytes. Meaning, it can store values from -2147483648 to 2147483647.

What is the difference between uint32 and unsigned int in C++? ›

uint32_t is used when you must have a 32 bit unsigned. int or unsigned int for general purposes when you don't need a guaranteed size and unsigned only if you can ensure that you won't have negative numbers.

What happens if you add 1 to an unsigned integer that is already at its maximum possible value? ›

With unsigned numbers the result is defined to "wrap around" to the other end of the integer's range, so if you add 1 to an integer that is at the maximum value for it's type, the result is zero.

What is special about an unsigned integer? ›

Unsigned Integers (often called "uints") are just like integers (whole numbers) but have the property that they don't have a + or - sign associated with them. Thus they are always non-negative (zero or positive). We use uint's when we know the value we are counting will always be non-negative.

What is 65535 in unsigned int? ›

65535 is the integer after 65534 and before 65536. It is the maximum value of an unsigned 16-bit integer.

Is 65535 unsigned short? ›

The maximum value of unsigned short is 65535 . One more than that is 65536 . "the value is converted by repeatedly... subtracting one more than the maximum value".

What is the maximum value of 4 bit unsigned integer? ›

With 4 bits, the maximum possible number is binary 1111 or decimal 15.

What is the maximum 8 bit integer in C++? ›

The max value 8 bits can hold is: 11111111 which is equal to 255.

What is the max 24 bit unsigned integer? ›

The range of unsigned integers that can be represented in 24 bits is 0 to 16,777,215 (FFFFFF16 in hexadecimal). The range of signed integers that can be represented in 24 bits is −8,388,608 to 8,388,607.

What is the point of unsigned integers? ›

Unsigned integers are used when we know that the value that we are storing will always be non-negative (zero or positive). Note: it is almost always the case that you could use a regular integer variable in place of an unsigned integer.

What happens if you assign to an unsigned int? ›

You simply cannot assign a negative value to an object of an unsigned type. Any such value will be converted to the unsigned type before it's assigned, and the result will always be >= 0.

Are unsigned integers slower? ›

In most cases, there is no difference in speed between using signed and unsigned integers. But there are a few cases where it matters: Division by a constant: Unsigned is faster than signed when you divide an integer with a constant.

What is the biggest unsigned int value? ›

A 32-bit unsigned integer. It has a minimum value of 0 and a maximum value of 4,294,967,295 (inclusive).

What is a unsigned integer example? ›

The simplest numbers that we want to represent in the machine are the unsigned integers. These are whole numbers without a sign, for example, 0, 1, 2, 3, … The mechanical calculators of yesteryear and the car mileage meter of today both store unsigned integers on what are effectively cogs having ten numbered teeth1.

Can an unsigned int equal 0? ›

An unsigned variable type of int can hold zero and positive numbers, and a signed int holds negative, zero and positive numbers. In 32-bit integers, an unsigned integer has a range of 0 to 232-1 = 0 to 4,294,967,295 or about 4 billion.

Can you assign int to unsigned int in C++? ›

You can convert an int to an unsigned int . The conversion is valid and well-defined. Since the value is negative, UINT_MAX + 1 is added to it so that the value is a valid unsigned quantity. (Technically, 2N is added to it, where N is the number of bits used to represent the unsigned type.)

Which helps you to avoid overflow when using unsigned integers? ›

Use 64-bits integers. One very good way to prevent integer overflows is to use int64_t to implement integers. In most case, 64-bits ints will not commit overflow, unlike their 32-bits counterparts. There is actually very few downsides in using int64_t instead of int32_t .

What happens when an unsigned int goes negative C++? ›

Unsigned integers are always 0 or positive. If they could go negative, they wouldn't be unsigned integers. In languages like C and C++, decrementing an unsigned integer containing the value 0 will just wrap around to the largest unsigned integer value.

Videos

1. C++ For C Programmers full Tutorial
(freeCodeAcademy)
2. C++ Variables
(Shmeowlex)
3. C++ variables, pointers, bitwise operations, arrays, vectors, and conditions
(Kobiljon T.)
4. CSC200 9/22/22 C++ Variables, Boolean Expressions, and Conditionals
(Michael Litman)
5. Taking a Byte Out of C++ - Avoiding Punning by Starting Lifetimes - Robert Leahy - CppCon 2022
(CppCon)
6. A Introduction to Handles (in C++)
(Dave Poo)

References

Top Articles
Latest Posts
Article information

Author: Aron Pacocha

Last Updated: 24/08/2023

Views: 5521

Rating: 4.8 / 5 (68 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Aron Pacocha

Birthday: 1999-08-12

Address: 3808 Moen Corner, Gorczanyport, FL 67364-2074

Phone: +393457723392

Job: Retail Consultant

Hobby: Jewelry making, Cooking, Gaming, Reading, Juggling, Cabaret, Origami

Introduction: My name is Aron Pacocha, I am a happy, tasty, innocent, proud, talented, courageous, magnificent person who loves writing and wants to share my knowledge and understanding with you.