Why does 'uint64_t i = -UINT64_MAX' have value '1'?

D

Dom Jackson

What is the value of -(max-val-of-an-unsigned-type)? According to my
compiler, -UINT32_MAX = 1, and -UINT64_MAX is also 1 (test code
below).

From the C99 LRM, p79:

"The result of the unary - operator is the negative of its (promoted)
operand. The integer promotions are performed on the operand, and the
result has the promoted type."

As far as I can make out from p43, uint32_t and uint64_t are unchanged
by the integer promotions. So how do you take the negative of an
unsigned operand, and how can the compiler get the result '1' without
using a temporary of a higher precision? Any thoughts?

Thanks -

Dom

--------------------------------------------------------------
[the code below is C++ to simplify data output, and produces this
result]:

i is ffffffff; j is 1; k is 1; l is ffffffffffffffff; -l is 1; m is 1

--------------------------------------------------------------

#define __STDC_LIMIT_MACROS
#include <stdint.h>
#include <iostream>
#include <iomanip>

int main() {
uint32_t i = UINT32_MAX;
int64_t j;
uint64_t k;
uint64_t l = UINT64_MAX;
uint64_t m = -UINT64_MAX;

j = -i;
k = -i;

std::cout << std::hex << std::setfill('0')
<< "i is " << i
<< "; j is " << j
<< "; k is " << k
<< "; l is " << l
<< "; -l is " << -l
<< "; m is " << m
<< std::endl;

return 0;
}
 
R

Richard Heathfield

Dom Jackson said:
What is the value of -(max-val-of-an-unsigned-type)?

In C (and presumably in C++ too), arithmetic on unsigned integer types
is carried out modulo the maximum value of the type + 1. If you try to
store a negative number in an unsigned integer type, exactly enough
"maximum value of the type, + 1"s are added to bring it into the range
representable by the type.

So let's pretend there's a four-bit unsigned integer type: unsigned
nibble. :) UNIBBLE_MAX is 15, so UNIBBLE_MAX + 1 is 16.

-UNIBBLE_MAX would be -15, so we have to add 16 to that to bring it into
the representable range, and -15 + 16 is 1.

Now let's think of an arbitrary unsigned integer type, unsigned arb.
UARB_MAX has some value or other, doesn't matter what.

-UARB_MAX is clearly negative, so we need to add enough lots of
(UARB_MAX + 1) to bring it into the representable range. One is enough,
so the resulting value is -UARB_MAX + UARB_MAX + 1, which is clearly 1,
irrespective of the value of UARB_MAX.

<C++ code snipped>

When cross-posting between comp.lang.c and comp.lang.c++, please ensure
that your code snippets are relevant to both groups. Thanks.
 
D

Dom Jackson

That's what I call service - thanks!

So let's pretend there's a four-bit unsigned integer type: unsigned
nibble. :)

Actually, that's precisely why I needed to know the answer, although
slightly more generally.
-UNIBBLE_MAX would be -15, so we have to add 16 to that to bring it into
the representable range, and -15 + 16 is 1.

Ahhh... I tried this with a 3-bit int, but I was trying to add 8 to
bit pattern '111', rather than to integer -7, which makes a lot more
sense.
When cross-posting between comp.lang.c and comp.lang.c++, please ensure
that your code snippets are relevant to both groups. Thanks.

I would have, if I'd known of portable printf conversions for 32-bit
and 64-bit ints - you don't happen to know the answer to that one?

Thanks -

Dom
 
R

Richard Heathfield

Dom Jackson said:
That's what I call service - thanks!

No sweat.

I would have, if I'd known of portable printf conversions for 32-bit
and 64-bit ints - you don't happen to know the answer to that one?

C provides two integer types that are guaranteed to be at least 32 bits
wide: long int, and unsigned long int. The printf function can display
long int correctly using "%ld", and unsigned long using "%lu". The
Standard doesn't mandate a maximum size for these types, so they might
easily be 64 bits wide on some systems, but 32 bits is all you're
guaranteed.

C99 (which hardly anyone has a compiler for) introduced two new integer
types that are guaranteed to be at least 64 bits wide: long long int
and unsigned long long int. Their printf format specifiers are,
respectively, "%lld" and "%llu". Don't hold your breath waiting for
long long and unsigned long long to become portable, though.
 
C

CBFalconer

Dom said:
.... snip ...


I would have, if I'd known of portable printf conversions for 32-bit
and 64-bit ints - you don't happen to know the answer to that one?

printf doesn't do such conversions. The format string tells it the
actual format of each parameter.

Follow-ups set to eliminate com.lang.c++. Bad cross-post.
 
R

Rolf Magnus

Dom said:
I would have, if I'd known of portable printf conversions for 32-bit
and 64-bit ints - you don't happen to know the answer to that one?

There is none for C++. In C(99), you can do:

#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>

int main()
{
int64_t value = 123;
printf("The value is %" PRId64 "\n", value);
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top