It is possible to print any integer value one wants, regardless of the floating point parameter:
printf("A: %d B: %6.2f\n", f, f + 0.15);
Here is how you can print arbitrary integers on Intel architecture:
int print_it(int, int /* nameless but printed */, float f)
{
printf("A: %d B: %6.2f\n", f, f + 0.15);
}
int main()
{
print_it(0, 12 /* will be printed */, 0.0);
print_it(0, 123 /* printed */, 1.1);
print_it(0, 1234 /* printed */ , 2.2);
}
This output:
A: 12 B: 0.00
A: 123 B: 1.10
A: 1234 B: 2.20
Explanation: Obviously, mismatched format string and parameters lead to undefined behavior. Nevertheless, sometimes this can be predicted. On Intel architecture, the first few parameters are passed by registers. Floating point values are passed on different registers.
Despite having the same printf
instruction as in the question, the output is different. What happens is that 12, 123, 1234 are passed through the general purpose register responsible for the second non-floating point parameter. Since printf
has only one non-floating point parameter, the register of the second non-fp parameter is unchanged. This register retains the value it got from the second parameter of print_it(0, int_value, fp_value)
.
But the original gives garbage:
for (f = 0.0; f <= 3; f += 1.1)
printf("A: %3f B: %6.2f\n", f, f + 0.15);
It gives different garbage because printf
calls other functions internally. These functions trash the general purpose register that printf("... %d ...", ...)
reads.
Obviously, this behavior happens only on systems that pass floating point parameters in a separate set of registers. Obviously, this happens only if the compiler optimization does not modify the code in some way, because it is allowed to do wild things when undefined behavior is at play.