I have a misunderstanding about the following statements:
printf("%d\n", -1 == 65535);
printf("%d\n", -1 == 4294967295UL);
printf("%d\n", -1 == 18446744073709551615);
printf("%d\n", -1 == 18446744073709551615UL);
The output is:
0
0
0
1
If if don't add the UL suffixes the compiler gives the following warning: main.c:36:28: warning: integer constant is so large that it is unsigned
Can someone explain this to me please? Why for 18446744073709551615 the result is 0 but for 18446744073709551615UL the results is 1? It's ok not to add that suffix?
Thanks
Case 3 exhibits a bug in GCC.
In GCC 11.2 for x86_64, the signed and unsigned int
types are 32 bits, the signed and unsigned long
and long long
types are 64 bits, and there are also extended types __int128_t
and __uint128_t
with width 128.
In -1 == 65535
, -1
and 65535
are both int
. They are unequal, so the comparison produces 0.
In -1 == 4294967295UL
, -1
is an int
, and 4294967295UL
is an unsigned long
. Per the usual arithmetic conversions, −1 is converted to unsigned long
, which wraps modulo 264, producing 18,446,744,073,709,551,615. That does not equal 4,294,967,295, so the comparison produces 0.
In -1 == 18446744073709551615
, 18446744073709551615
is too large for the normal types. C 2018 6.4.4.1 5 says the type of an unsuffixed decimal integer constant is the first of int
, long int
, or long long int
that can represent it. Since none of those can represent it in this C implementation, paragraph 6 applies:
If an integer constant cannot be represented by any type in its list, it may have an extended integer type, if the extended integer type can represent its value. If all of the types in the list for the constant are signed, the extended integer type shall be signed…
From this, we see the type of 18446744073709551615
should be __int128_t
. This is confirmed by using _Generic
to reveal the type; the following program prints “__int128_t”:
#include <stdio.h>
int main(void)
{
printf("%s\n", _Generic(18446744073709551615,
__int128_t: "__int128_t",
default: "something else")
);
}
In contrast, Clang 13.0.0 uses unsigned long long
.](https://godbolt.org/z/a5Y3h5Wbo)
Thus, GCC’s error message that states “integer constant is so large that it is unsigned” is incorrect. GCC uses a different type than its error message states, so this is a bug in GCC.
Given that the type is __int128_t
, −1 and 18,446,744,073,709,551,615 are both representable in the type, so evaluation of the expression converts −1 to __int128_t
with no change in value, the values are unequal, and the comparison produces 0.
(Since Clang uses unsigned long long
, it produces 1.)
For confirmation, we see that printf("%d\n", -18446744073709551615 < 0);
prints “1” in GCC, showing the comparison was performed with a signed type, whereas Clang prints “0”.
Finally, in -1 == 18446744073709551615UL
, the usual arithmetic conversions convert −1 to unsigned long
, which wraps modulo 264, producing 18,446,744,073,709,551,615. Then the two operands are equal, and the comparison produces 1.