-2

Recently, I am playing around with C to test different things.

printf("%d", 0.4);

would print -1717986918.

I understand that this must be related to the binary representation of a floating-point number. However, after using some online resources to convert 0.4 into a float the binary representation does not match with -171986918. Please explain what really happened behind this piece of code.

PS: I am using gcc compiler and running it on Windows 64 bit.

CheeseS
  • 21
  • 4
  • 2
    `sizeof(int)` and `sizeof(double)` (which `0.4` probably is) have a different number of bytes (most likely). – Fiddling Bits Dec 20 '20 at 01:33
  • @FiddlingBits Thank you, apparently this conversion omits the first 16 bits of the double value. One follow-up question, would it be safe to say that C uses double as the "default" float type then? – CheeseS Dec 20 '20 at 01:41
  • 11
    The constant `0.4` has type `double`. As such, it is not properly matched with the `%d` directive, with the result that your `printf` call has **undefined behavior**. What "actually happens" depends on details of your implementation of `printf`, and possibly on other factors as well. This is not a useful detail to study, beyond learning the lesson that you must properly type match your `printf` arguments with the format string. Whatever specifics you might figure out would not be reliably transferrable. – John Bollinger Dec 20 '20 at 01:54
  • 2
    Note that if you enabled warnings (e.g. `gcc -Wall`), the `printf` statement would be flagged by the compiler. – Craig Estey Dec 20 '20 at 02:12
  • 2
    Some systems — macOS Mojave 10.14.6 is one, but I believe Linux x86/64 is another — have an ABI that passes integers and doubles in different sets of registers. That means if you don't have the compiler set to fussy, you get the "correct" result from `printf("Integer: %4d; double: %13.6g\n", 3.141592654, 9876);` — that is, it produces "Integer: 9876; double: 3.14159`, give or take some spaces. That's because when `printf()` processes the `%d`, it pulls the first integer out of the integer registers, and when it processes `%13.6g`, it pulls the first double out of the floating point registers. – Jonathan Leffler Dec 20 '20 at 02:33
  • 1
    You can't rely on that behaviour. Most historical systems would not behave like that. But (at least some of) the x86/64 ABIs in use lend themselves to this abuse. – Jonathan Leffler Dec 20 '20 at 02:35
  • [C11 Standard - 7.21.6.1 The fprintf function(p9)](http://port70.net/~nsz/c/c11/n1570.html#7.21.6.1p9) also requires the conversion specifiers be the correct type for the arguments -- so the just because compiling with non-fussy options may allow it to work sometimes -- doesn't mean it is well-formed code `:)` Output of `-1717986918` likely means the `RSI` register is uninitialized. – David C. Rankin Dec 20 '20 at 04:31
  • @FiddlingBits without any suffixes then a floating-point literal is always `double`. `f` and `L` make it `float` and `long double` respectively – phuclv Dec 20 '20 at 06:27
  • Next time, compile with `gcc -Wall -Wextra -g` – Basile Starynkevitch Dec 20 '20 at 07:13

1 Answers1

4

I understand that this must be related to the binary representation

No, it is undefined behavior (UB).


"%d" with a double does not match.

If a conversion specification is invalid, the behavior is undefined. C17dr § 7.21.6.1 9

The output and behavior is not defined (UB) by the language. Anything may happen.


On some systems, the UB might see a portion of the binary pattern interpreted as an int.

On another systems, int and double are passed in different ways and the output seen is based on garbage.

On another system or just another day, bad things happen.

It is all undefined behaviors (UB).


explain what really happened

To get an idea of what might have happened in OP's machine, use hexadecimal outputs.

printf("%x\n", (unsigned) -1717986918);  // UB to print a negative  `int` with `"%x"`
printf("%a\n", 0.4);

9999999a
0x1.999999999999ap-2

So it looks like the least significant 4 bytes of 0.4 significand were interpreted as the int.
Still UB. Anything may happen.

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