What Happens If I Assign a Negative Value to an Unsigned Variable

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



Leave a reply



Submit