2

I found a few questions on this particular topic, but they were about C++.

How portable is casting -1 to an unsigned type?

converting -1 to unsigned types

Is it safe to assign -1 to an unsigned int to get the max value?

And when reading the answers, it seemed likely or at least not unlikely that this is one of those things where C and C++ differs.

The question is simple:

If I declare a variable with unsigned char/short/int/long var or use any other unsigned types like fixed width, minimum width etc, is it then guaranteed that var = -1 will set var to the maximum value it can hold? Is this program guaranteed to print "Yes"?

#include <stdio.h>
#include <limits.h>

int main(void) {
    unsigned long var = -1;
    printf("%s\n", var == ULONG_MAX ? "Yes" : "No");
}
klutt
  • 30,332
  • 17
  • 55
  • 95
  • 3
    but really, isn't it a duplicate of that https://stackoverflow.com/questions/1667963/how-portable-is-casting-1-to-an-unsigned-type ? I assumed it's not, because you mentioned it, but that answer literally says `The requirements on unsigned arithmetic guarantee that casting -1 to an unsigned type will produce the largest number possible for the target type` and the answer also follows with `This is the same in C and C++`. – KamilCuk Jan 28 '21 at 09:52
  • @KamilCuk I think it's best to keep this, as it focus on C only, and your answer is very good and straight to the point with references. – klutt Jan 28 '21 at 09:55
  • @KamilCuk it even explicitly refers to C99 standard: `C99, §6.2.5/9` so it's definitely valid for C. – junix Jan 28 '21 at 09:57
  • Well, we now have the definite C version – Antti Haapala -- Слава Україні Jan 28 '21 at 11:45
  • @AnttiHaapala I think it's good to have. This time I seem to have missed it, but there are numerous times when I have wondered something about C and I see loads of questions tagged with both languages, where the answers for C are hard to find if they exist at all. – klutt Jan 28 '21 at 11:53
  • 1
    @KamilCuk I'm personally not comfortable with voting to close a C question as a dupe to a C++ question just because an answer on the C++ question mentions that it's applicable to C also. – Andrew Henle Jan 28 '21 at 12:11

2 Answers2

7

Is it guaranteed that assigning -1 to an unsigned type yields the maximum value?

Yes.

Is this program guaranteed to print "Yes"?

Yes.

It's a conversion, from int -1 to unsigned long. -1 can't be represented as unsigned long. From C11 6.3.1.3p2:

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

so we add one (ULONG_MAX + 1) to -1 and we get -1 + (ULONG_MAX + 1) = ULONG_MAX which is in range of unsigned long.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
2

The conversion from -1 (signed int) to a large unsigned type is well-defined, as per C17 6.3.1.3.

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

This comes with a helpful foot note 60)

  1. The rules describe arithmetic on the mathematical value, not the value of a given type of expression.

So given unsigned long var = -1;, the value becomes

  • -1 then adding one more than maximum value of unsigned long
  • Meaning -1 + ULONG_MAX+1 = U_LONG_MAX.

This is well-defined and portable behavior. Unlike conversions from unsigned to signed, that can invoke implementation-defined behavior.

Also, this is regardless of signedness format, since the mathematical value should be used, not the raw binary one. Had you done something like unsigned long var = (signed long)0xFFFFFFFFF; then that would be another story in case of exotic/fictional systems with 1's complement or signed magnitude.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Alternatively you can use `stdint.h` types instead, since those are guaranteed to use 2's complement. `uint32_t var = (int32_t)0xFFFFFFFFF;` is well-defined in itself, but dysfunctional exotic systems need not actually provide `int32_t`, it's an optional type. – Lundin Jan 28 '21 at 11:57