views:

312

answers:

6

In K&R ANSI C book, section A.7.4.5 (Unary Minus Operator) it is stated:

... The negative of an unsigned quantity is computed by subtracting the promoted value from the largest value of the promoted type and adding one; ...

How exactly is this calculated? Could you give a short C example? I don't see how this could yield the negative of, say, 200u: subtracting 200 from a max value of any integral type (signed or unsigned) and adding 1 does not result in -200.

Note that I do know what the unary minus does - the problem is that I don't see how the result is calculated according to the description.

+7  A: 

Unsigned values can't be negative, so -200 is not a possible result.

What it's saying is that if UINT_MAX is 65535 on your system, then the result of:

unsigned a = 200;
unsigned b = -a;
long c = -a;

will leave 65336 in both b and c.

If your system has UINT_MAX > LONG_MAX (typically because int and long are the same size), you will need to use long long for c (although note that there's not even any guarantees that that's long enough).

This detail (that the result of negating an unsigned number is another, necessarily positive, unsigned number) can result in some unexpected effects if you do not understand it. For example, in this code the first example prints "true" but the second example prints "false":

int a = 200;
unsigned b = 200;
if (-a < 100) printf("true\n"); else printf("false\n");
if (-b < 100) printf("true\n"); else printf("false\n");

(note that we are not storing the result of the negation operator anywhere - that is not the issue).

caf
I'm not asking about storing negative values in unsigned types.
Ree
@Ree: I doesn't matter where you *store* it. When you apply the arithmetical operation to an *unsigned* value, the entire evaluation is performed as *unsigned* evaluation. Which means, once again, that you *never* get a negative value in your case. Again, `-200u` is *not* negative. You can actually store it in an 'long int' variable (signed), and you will still get a *positive* value.
AndreyT
Thanks AndreyT, I've added the `long` example to clear that up.
caf
+5  A: 

It describes operations to implement modular arithmetic, i.e. it computes a value such that

a + (-a) == 0

This makes the negated unsigned number behave closely to a negated signed number.

On machines where the number representation is two's complement (such as x86), this is done by just treating the bit pattern of the unsigned number as an ordinary signed number, and using the machine's standard "negate" instruction.

unwind
Why does the book describe two's complement system when C could be used with other representations as well? If you had a one's complement machine the result would be -0 (which would be actually just a 0).
Ree
The answer is incorrect. Firstly, the quote is talking about *unsigned* values. There's no such thing as "2's complement" for unsigned values. The notion of signed representation is only applicable to signed values. Secondly, the behavior described in the quote is the standard behavior of unsigned values (modulo arithemetic), which is not tied in any way to any specific representation. The machine can be ternary, as far as I'm concerned.
AndreyT
... Additionally, the actual value of `-1u` is, of course, `65535u` (in an implementation with UINT_MAX = 65535) , not `-1`, which is the entire point of the quote in the original post.
AndreyT
AndreyT's comment is absolutely spot-on, especially with regard to the implications of this clause for non-binary integer types.
Stephen Canon
I don't think deleting is necessary; in fairness to you, the modulo behavior is not dissimilar from twos-complement, and many people use the term relatively interchangeably. A little bit of clarification wouldn't be misplaced, though =)
Stephen Canon
They only thing it has to do with 2's complement is that on a 2's complement machine modulo arithmetic is purely conceptual. It requires no efforts from the compiler, it just turns out that way by itself, automatically (which is what your answer illustrates with an example). Nevertheless, this is nothing more than an implementational detail, not formally tied to the specification of the language in any way.
AndreyT
Once again, with your assembly example you only show that on a 2's complement machine modulo arithmetic is conceptual and requires no additional code. I.e. the compiler does not need to care whether the value is signed or unsigned (until we get do multiplication/division). This is an implementational detail, which has no meaning at the level of C language.
AndreyT
AndreyT: The compiler also has to care for comparisons. `-200 < 100` is true, but `-200u < 100` is false.
caf
@AndreyT, Stephen: thanks. I think I got it, now.
unwind
+2  A: 

Let's keep it simple and look at an unsigned char... 8 bits with a value range of 0-255.

What is (unsigned char)-10 and how it it calculated?

Going by the K & R statement you quote, we have:

promoted value of -10 is 10 subtracted from largest value of promoted type is 255 plus 1 = 246

so (unsigned char)-10 is actually 246. Does that make sense?

Gene Goykhman
+4  A: 

Another question has touched this topic already

Example

unsigned char i = -10;
printf("%u\n",i);

Result

246
Peter Lindqvist
+2  A: 

Apparently you missed the word unsigned in the description you quoted. This is the key word in this case. In C language the "negative" of an unsigned quantity is still unsigned, meaning that it is not really negative. Unsigned values can't ever be negative, by definition. They are always positive or 0. Arithmetic of unsigned values in C is modulo arithmetic or, in simple words, unsigned quantities "wrap around" when you perform arithmetical operations on them. Unary negation is not an exception. Calculating -n when n is unsigned is no different from calculating 0 - n. If n is unsigned int and its value is 200 the expected result is not -200, but rather UINT_MAX - 200 + 1, which is exactly what the quote is telling you.

AndreyT
I am marking your answer accepted since you pointed out my main mistake - I thought that after evaluation `-200u` became a signed quantity by being promoted to a signed integer capable of storing the value.
Ree
+2  A: 

Operations on unsigned integral types use modular arithmetic. Arithmetic modulo m is much the same as regular arithmetic, except that the result is the positive remainder when divided by m, if you haven't run into it at school (for more details, see the Wikipedia article. For example, 7 - 3 modulo 10 is 4, while 3 - 7 modulo 10 is 6, since 3 - 7 is -4, and dividing it by 10 yields a quotient of -1 and a remainder of 6 (it also could be expressed with a quotient of 0 and a remainder of -4, but that's not how it works in modular arithmetic). The possible integer values modulo m are the integers from 0 to m-1, inclusive. Negative values are not possible, and -200 isn't a valid unsigned value under any circumstances.

Now, a unary minus means a negative number, which isn't a valid value modulo m. In this case, we know that it's between 0 and m-1, because we're starting with an unsigned integer. Therefore, we're looking at dividing -k by m. Since one possible value is a quotient of 0 and a remainder of -k, another possible is a quotient of -1 and remainder of m-k, so the correct answer is m-k.

Unsigned integers in C are normally described by the maximum value, not the modulus, which means that an unsigned 16-bit number would normally be described as 0 to 65535, or as having a maximum value of 65535. This is describing the values by specifying m-1 rather than m.

What the quotation you have says that the value of a negative is taken by subtracting it from m-1 and then adding 1, so -k is m - 1 - k + 1, which is m - k. The description is a little roundabout, but it specifies the correct result in terms of the pre-existing definitions.

David Thornley
+1 for a nice explanation.
Ree