Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why this ambiguous behaviour of "printf()"?

I have intentionally forced printf() to print celsius as an int (Using %8d format specifier for it). I know that this is the reason of printing 0 (under the heading Celsius Scale).

But I just want to know why fahr is going to be printing 0.0 in the whole table.

I have used GCC compiler.

This is the code for Celsius to Fahrenheit Conversion :

#define LOWER 0.0F
#define UPPER 300.0F
#define STEP 20.0F

#include <stdio.h>

void main() {
    float celsius, fahr;
    printf("*Conversion from Celsius to Fahrenheit*\n");
    printf("Celsius Scale \t   Fahrenheit Scale\n");
    for (celsius = LOWER; celsius <= UPPER; celsius = celsius + STEP) {
        fahr = (9.0f * celsius / 5.0f) + 32.0f; 
        printf("%8d\t\t%5.1f\n", celsius, fahr);
    }
}

This following table is the output of the above code :

*Conversion from Celsius to Fahrenheit*
Celsius Scale      Fahrenheit Scale
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
       0                  0.0
like image 249
Lakshay Avatar asked Dec 08 '22 10:12

Lakshay


1 Answers

You have undefined behaviour (which means it is not 'predictable' what the results of an operation will be), as you are passing a float argument to printf when it expects an int argument (for the %8d) format.

From this C11 Draft Standard:

7.21.6 Formatted input/output functions
...
9 If a conversion specification is invalid, the behavior is undefined. If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

Exactly why the fahr values are shown incorrectly is difficult to say with any certainty, but it's possibly connected to the fact the the float arguments are promoted to double, which has a different size than the expected int type, and thus, the call stack (or call 'frame' if arguments are passed in registers) is corrupted.

It is perhaps worth noting that, on my system (MSVC, 64-bit), running your code displays zeros for the celsius fields but the correct values for fahr. (But compiling for a 32-bit target does reproduce your problem!)

To fix the problem, explicitly cast the celsius argument to an int:

void main()
{
    float celsius, fahr;
    printf("*Conversion from Celsius to Fahrenheit*\n");
    printf("Celsius Scale \t   Fahrenheit Scale\n");
    for (celsius = LOWER; celsius <= UPPER; celsius = celsius + STEP) {
        fahr = (9.0f * celsius / 5.0f) + 32.0f;
        printf("%8d\t\t%5.1f\n", (int)celsius, fahr);
    }
}
like image 162
Adrian Mole Avatar answered Dec 28 '22 18:12

Adrian Mole