1

(4294967296=2^32)

I want to print 4294967296 using printf("%u, %d") and want to see what will happen but i get some strange result.

The code is as following:

printf("%d\n", 4294967296);

printf("d = %u = %d = 0x%x  \n", 4294967296, 4294967296, 4294967296);//this is strange

printf("d = %u = %d = 0x%x  \n", 4294967295 + 1, 4294967295 + 1, 4294967295 + 1);

and my result is

0

d = 0 = 1 = 0x0

d = 0 = 0 = 0x0

the picture of result

you can see the binary expression of 4294967296 is 0x0 in the second line, but when i print 4294967296 use "%d", it will show 1, which i am confused with. I expect the output of 1 to be 0.

Sisir
  • 4,584
  • 4
  • 26
  • 37
Jackson
  • 53
  • 5
  • `printf("%d\n", 4294967296);` invokes *undefined behavior* [C11-Standard 7.21.6.1 The fprintf function(p9)](http://port70.net/~nsz/c/c11/n1570.html#7.21.6.1p9). – David C. Rankin Sep 18 '19 at 06:34
  • The %d and %u formatting arguments expect a 32-bit argument to be passed to printf(). But the code actually passes 64-bit arguments, 0x0000000100000000 in hex. So the %d makes it read the lower 32-bits of that value, which is 0. The next %u makes it read the upper 32-bits of that value, which is 1. Little-endian machine btw. Doesn't happen in the second statement since 4294967295 + 1 produces a 32-bit value. If your compiler doesn't warn about this then be sure to either update it or crank up the warning level. – Hans Passant Sep 18 '19 at 07:42
  • @HansPassant "4294967295 + 1 produces a 32-bit value" --> depends on C89 vs C99. With modern C, 4294967295 is also a `long long` and `4294967295 + 1` is a 64-bit value. – chux - Reinstate Monica Sep 18 '19 at 07:51

3 Answers3

5

If supported then the value 4294967296 is a long long, because it doesn't fit in an unsigned int.

The format "%d" is for int (a signed int).

Mismatching value type and format specifier leads to undefined behavior.

The value 4294967295 does fit in an unsigned int but then you add 1 making it overflow.

The range of a 32-bit unsigned integer is 0 to 2³²-1 (inclusive).

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 2
    "The value 4294967295 does fit in an unsigned int" is true, yet `4294967295` is still a `long long` _decimal constant_ since C99. "add 1 making it overflow" --> No. that sum is `long long` 4294967296. Your answer makes sense for C89 where `4294967295` is an `unsigned long` (and adding 1 is well defined). – chux - Reinstate Monica Sep 18 '19 at 07:47
1

Unsigned integer never overflow:

(C99, 6.2.5p9) "A computation involving unsigned operands can never 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 type."

But in C, integer overflows are undefined behavior:

(C99, 6.5.5p5) "If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined."

(C99, 3.4.3p3) "EXAMPLE An example of undefined behavior is the behavior on integer overflow."

I'm quoting these from an answer to a question that I asked previously.

Imran Rana
  • 11,899
  • 7
  • 45
  • 51
1

printf(“%d\n”, 4294967296); makes some strange errors

Yes it does - a most interesting question.


This comes down to what is the type of 4294967295 and 4294967296?

Is the type determined by fitting in:
int, long, unsigned long (C89)
int, long, long long (C99)
int, long, unsigned long, long long, unsigned long long (Perhaps VS2017)

Note: 4294967295 + 1 is not signed integer overflow in any case.


This is one of the places where C89, C99 (or later) differ and where VS2017 does its own thing.


With compliant C89, (32-bit world) 4294967295 is a unsigned long decimal constant and adding 1 is unsigned long 0. (C89 6.1.3.2)

As ... arguments, an int 0 and unsigned 0 both work for "%u" and "%d" and the value of 0 is in range for both. Yet this is an unsigned long 0 and so technical undefined behavior (UB). On 32-bit systems, this UB is often benign and results in the same as printing a unsigned 0.

4294967296 is simply an out of range constant.


With C99 (or later - certainly not OP's case), 4294967295 is a long long decimal constant1 and adding 1 is simply long long 4294967296. 4294967296 is another long long decimal constant

Passing a long long to match a "%d" or "%u" is undefined behavior. The same output of "0, 1, 0" in both cases would be an "expected" UB.


Yet OP is using VS2017 and 4294967295 is a unsigned long decimal constant (per C89 rules) and int, long int, or unsigned long int and adding 1 is unsigned long 0. Printing that with %u,%d is technically UB, but with 32-bit unsigned having the same characteristic as 32-bit unsigned long the 0,0,0 output is reasonable.

4294967296 is apparently some 64-bit type (such as long long)2 and per C99 rules is UB. Output of 0, 1, 0 is reasonable, yet not resilient.

OP's result may differ depending on /Za, /Ze (Disable Language Extensions).


1 or a 64-bit long.

2 I was unable to find a definitive on-line VS2017 ref for the type of a C decimal constant, (not C++) for values more than 4294967295.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256