5

In C++ primer it says that "if we assign an out of range value to an object of unsigned type the result is the remainder of the value modulo the number of values the target type can hold."

It gives the example:

int main(){


unsigned char i = -1;
// As per the book the value of i is 255 .
}

Can anybody please explain it to me how this works.

vyasriday
  • 90
  • 1
  • 9

5 Answers5

8

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).

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • But I am not able to get 255 as output of my code when i try -1 % 256 it gives me -1. – vyasriday Dec 22 '16 at 06:45
  • "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)." – vyasriday Dec 22 '16 at 06:49
  • 1
    Integer literals are of type `int` by default, and types smaller than `int` are *promoted* to type `int` when they are used in expressions. Therefore, the expression `-1 % 256` is going to give a result that is an `int`. -1 can be represented as an `int`, so it will be the answer. This is completely different from what your question was about, which is storing a value of `-1` in an `unsigned char` type. – Cody Gray - on strike Dec 22 '16 at 06:52
  • Ok this is because the value given was 255 but i was not able to understand how that was happening i.e why I asked about -1 %256. – vyasriday Dec 22 '16 at 06:53
  • But I am still not able to get that it gives 255 i.e I assigned -1 to unsigned char. So what actually is the meaning of saying it gives 255 by using the method of modulo. – vyasriday Dec 22 '16 at 06:56
  • 1
    Oh, I see your confusion. You are trying to execute this code in C++, but the `%` operator there is not exactly modulo—it is *remainder*, and has slightly different semantics. If the first argument is negative, a negative number will be returned. You want the logical, arithmetic modulo operation. – Cody Gray - on strike Dec 22 '16 at 07:02
  • Ok ok now I finally understood why I was not getting it. – vyasriday Dec 22 '16 at 07:14
2

It's equivalent in C to C++, though worded differently:

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 by the new type until the value is in the range of the new type.

3 Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

Community
  • 1
  • 1
user3553031
  • 5,990
  • 1
  • 20
  • 40
1

The literal 1 is of type int. For this explanation, let's assume that sizeof(int) == 4 as it most probably is. So then 1 in binary would look like this:

00000000 00000000 00000000 00000001 

Now let's apply the unary minus operator to get the -1. We're assuming two's complement is used as it most probably is (look up two's complement for more explanation). We get:

11111111 11111111 11111111 11111111

Note that in the above numbers the first bit is the sign bit.

As you try to assign this number to unsigned char, for which holds sizeof(unsigned char) == 1, the value would be truncated to:

11111111

Now if you convert this to decimal, you'll get 255. Here the first bit is not seen as a sign bit, as the type is unsigned.

DeiDei
  • 10,205
  • 6
  • 55
  • 80
  • Using a bit pattern other than `11111111 11111111 11111111 11111111` would be more clear as to where the `11111111` originated. – chux - Reinstate Monica Dec 22 '16 at 07:18
  • This is wrong. Representation of the signed integer type doesn't affect the result of the unsigned type, it's value does. – 2501 Dec 22 '16 at 09:22
  • @2501 If compiler would create code that will HIDE actual values from programmer, it would require to use different language just make a n API for working with direct modeled objects. Those who try to propose otherwise, are like that kind of guy who would take bottle of water with citric acid and yellow paint in it and say that it is lemon juice because label says so, it doesn't matter how it was made. Anyway current standard's statements ARE based on _actual_ representation of integers on existing platforms, they were just explained in abstract mathematics manner for shortness – Swift - Friday Pie Dec 23 '16 at 06:50
  • I'm not nitpicking. You're factually wrong. Representation of -1 in two's complement is `11111111`. Representation of -1 in one's complement is `11111110`. The result of converting value -1 to an unsigned integer will always give the maximum possible result, where all bits are set: `11111111`. Using your method will give different results based on integer representation, which is obviously not the case. – 2501 Dec 23 '16 at 09:44
0

In Stroustrup's words:

If the destination type is unsigned, the resulting value is simply as many bits from the source as will fit in the destination (high-order bits are thrown away if necessary). More precisely, the result is the least unsigned integer congruent to the source integer modulo 2 to the nth, where n is the number of bits used to represent the unsigned type.

Excerpt from C++ standard N3936:

For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type: “unsigned char”, “unsigned short int”, “unsigned int”, “unsigned long int”, and “unsigned long long int”, each of which occupies the same amount of storage and has the same alignment requirements (3.11) as the corresponding signed integer type47; that is, each signed integer type has the same object representation as its corresponding unsigned integer type.

Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
0

I was going through the excerpt from C++ primer myself and I think that I have kind of figured out a way to mathematically figure out how those values come out(feel free to correct me if I'm wrong :) ). Taking example of the particular code below.

unsigned  char c = -4489;
std::cout << +c << std::endl; // will yield 119 as its output

So how does this answer of 119 come out?

well take the 4489 and divide it by the total number of characters ie 2^8 = 256 which will give you 137 as remainder.

4489 % 256 = 137.

Now just subtract that 137 from 256.

256 - 137 = 119.

That's how we simply derive the mod value. Do try it for yourself on other values as well. Has worked perfectly accurate for me!