What happens when I assign a negative value to an unsigned int?
The "%d"
format is for (signed) int values. If you use it with an unsigned value, it could print something other than the actual value. Use "%u"
to see the actual value, or %x
to see it in hexadecimal.
In the declaration
unsigned int x = -1;
the expression -1
is of type int, and has the value -1. The initializer converts this value from int to unsigned int. The rules for signed-to-unsigned conversion say that the value is reduced modulo UINT_MAX + 1
, so -1
will convert to UINT_MAX
(which is probably 0xffffffff
or 4294967295
if unsigned int
is 32 bits).
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.
What happens if I assign a negative value to an unsigned variable?
For the official answer - Section 4.7 conv.integral
"If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where
n
is the number of bits used to represent the unsigned type). [ Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). —end note ]
This essentially means that if the underlying architecture stores in a method that is not Two's Complement (like Signed Magnitude, or One's Complement), that the conversion to unsigned must behave as if it was Two's Complement.
Assign a negative number to an unsigned int
How can an
unsigned int
store a negative number?
It doesn't. Instead, it stores a representable number that is congruent with that negative number modulo the number of all representable values. The same is also true with results that are larger than the largest representable value.
Is it save to be used or does this yield undefined behaviour?
There is no UB. Unsigned arithmetic overflow is well defined.
It is safe to rely on the result. However, it can be brittle. For example, if you add -22u
and 100ull
, then you get UINT_MAX + 79
(i.e. a large value assuming unsigned long long
is a larger type than unsigned
) which is congruent with 78 modulo UINT_MAX + 1
that is representable in unsigned long long
but not representable in unsigned
.
Note that signed arithmetic overflow is undefined.
What will happen if I assign negative value to an unsigned char?
the result is the remainder of the value modulo the number of values the target type can hold
Start with "the number of values the target type can hold". For unsigned char
, what is this? The range is from 0 to 255, inclusive, so there are a total of 256 values that can be represented (or "held").
In general, the number of values that can be represented in a particular unsigned integer representation is given by 2n, where n is the number of bits used to store that type.
An unsigned char
is an 8-bit type, so 28 == 256, just as we already knew.
Now, we need to perform a modulo operation. In your case of assigning -1 to unsigned char
, you would have -1 MOD 256 == 255
.
In general, the formula is: x MOD 2n, where x is the value you're attempting to assign and n is the bit width of the type to which you are trying to assign.
More formally, this is laid out in the C++11 language standard (§ 3.9.1/4). It says:
Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer.*
* This implies that unsigned arithmetic does not 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 unsigned integer type.
Perhaps an easier way to think about modulo arithmetic (and the description that you'll most commonly see used) is that overflow and underflow wrap around. You started with -1, which underflowed the range of an unsigned char
(which is 0–255), so it wrapped around to the maximum representable value (which is 255).
Why is the unsigned variable storing negative value?
It works because this:
unsigned int x = -1;
causes the int
-typed literal -1
to be converted to an unsigned int
which is a standard, well-specified, conversion. The draft C11 spec says:
6.3.1.3 Signed and unsigned integers
1 When a value with integer type is converted to another integer type other than
_Bool, if the value can be represented by the new type, it is unchanged.2 Otherwise, if the new type is unsigned, the value is converted by repeatedly
adding or
subtracting one more than the maximum value that can be represented in the new
type until the value is in the range of the new type.60)
The latter is what is happening here, so assuming 32-bit integers 232 is being added, making the result 0xffffffff
.
I don't believe that the printf()
with %u
prints -1
, that would be a rather major bug.
Assigning negative numbers to an unsigned int?
The -12
to the right of your equals sign is set up as a signed integer (probably 32 bits in size) and will have the hexadecimal value 0xFFFFFFF4
. The compiler generates code to move this signed integer into your unsigned integer x
which is also a 32 bit entity. The compiler assumes you only have a positive value to the right of the equals sign so it simply moves all 32 bits into x
. x
now has the value 0xFFFFFFF4
which is 4294967284
if interpreted as a positive number. But the printf
format of %d
says the 32 bits are to be interpreted as a signed integer so you get -12
. If you had used %u
it would have printed as 4294967284
.
In either case you don't get what you expected since C language "trusts" the writer of code to only ask for "sensible" things. This is common in C. If you wanted to assign a value to x
and were not sure whether the value on the right side of the equals was positive you could have written unsigned int x = abs(-12);
and forced the compiler to generate code to take the absolute value of a signed integer before moving it to the unsigned integer.
Related Topics
Is #Pragma Once a Safe Include Guard
How to Use Cout ≪≪ Myclass
Accessing Class Members on a Null Pointer
Developing C Wrapper API For Object-Oriented C++ Code
Are the Days of Passing Const Std::String & as a Parameter Over
How to Erase an Element from Std::Vector≪≫ by Index
Why Does the C++ Map Type Argument Require an Empty Constructor When Using []
Will Std::String Always Be Null-Terminated in C++11
Using Custom Std::Set Comparator
Does Initialization Entail Lvalue-To-Rvalue Conversion? Is 'Int X = X;' Ub
Do I Need to Cast to Unsigned Char Before Calling Toupper(), Tolower(), Et Al.
C++ Lambda With Captures as a Function Pointer
Uninitialized Variable Behaviour in C++
Why Is Transposing a Matrix of 512X512 Much Slower Than Transposing a Matrix of 513X513
Conveniently Declaring Compile-Time Strings in C++
Is "Argv[0] = Name-Of-Executable" an Accepted Standard or Just a Common Convention