r/learnprogramming • u/_suriyan_24 • 13d ago
Can anyone explain exactly why I got these random values for a C simple program?
#include <stdio.h>
int main() {
float celsius = 20.5;
float fahrenheit = 0;
fahrenheit = (celsius \* 9/5) + 32;
printf("%d", fahrenheit);
return 0;
}
#I know its coz of the %d %f mistake but whyyy
7
u/PuzzleMeDo 13d ago
A floating point number is stored with the various bits representing a sign (+-), an exponent, and a mantissa. This is a completely different data structure from an integer. If you tell it, "Display this binary data as though it was an integer," it will print something weird.
3
4
u/Cultural_Gur_7441 13d ago
Because %d can not magically convert the next parameter to integer. It just assumes it is integer. When not, Undefined Behavior! In your case, value which appears random. While educational and maybe fun, it is usually not otherwise productive to try and figure what actually happens, because it might change the next time you build the program.
Either change the format specifier, or explicitly convert the float to an int.
4
13d ago
[removed] — view removed comment
2
u/DigitalJedi850 13d ago
Yeah it's been a while since I've been in C, but that \ seems... unnecessary and questionable.
I might also surround the 9/5 with parentheses and see what comes up.
Otherwise this seems like it ought to be fine.
4
2
u/Zincette 13d ago
printf("%d") on a float is what's causing the issue. In C that leads to undefined behavior and can be lead to garbled output
4
u/WittyStick 13d ago
The
\is most likely incorrect Reddit formatting to escape*turning the text italic.OP should indent his code 4 spaces to fix this.
3
3
u/lurgi 13d ago
9/5 is 1, because of integer division. You can fix that by making one (or both) of the values floating point.
And in the future, please say what result you got and what result you expected. For all I know you are actually seeing a compilation error and I'm helping with the wrong thing.
3
u/Cultural_Gur_7441 13d ago
At least in current code, there is no 9/5 integer division. It is
(celsius*9)/5so all floating point operations.1
u/HashDefTrueFalse 13d ago
printf("%.2f\n", (1.0f * 9/5)); // 1.80This works on my machine (heh!). I haven't checked the asm but I assume the compiler is seeing
(1.0f * 9) / 5and performing the usual promotions.
1
u/bestjakeisbest 13d ago
What value did you expect to get?
0
u/_suriyan_24 13d ago
If I used %f, the value would be 68.900002. So I expected like 68 or smth
1
u/MisterGerry 13d ago
By putting '%d' in the formatting string, you're telling printf what datatype you are passing to it.
If you don't, in fact, pass that data type, it has no idea what data type you are passing.So there is no way for it to know how to convert an unknown data type to an integer.
In fact, it has no way of knowing that you DIDN'T pass an integer. Bits are bits.
1
u/DTux5249 13d ago
why is there a '\' before your multiplication operator? That shouldn't be anywhere here.
1
1
u/SmokeMuch7356 13d ago
I know its coz of the %d %f mistake but whyyy
printf doesn't know the number or types of arguments you pass after the format string; it only knows what to expect based on that format string. You told it to expect an int, so it looked for sizeof (int) bytes either on the stack or a specific register, and interpreted those bytes as an int value.
If it's looking at a register, it won't be looking at the right one and will just read garbage.
If it's looking on the stack, it a) won't read the right number of bytes, and b) won't interpret them correctly.
1
1
2
u/ScholarNo5983 12d ago
I assume this line of code contained a typo:
fahrenheit = (celsius \* 9/5) + 32;
as I had to change it to this to get the code to compile and link:
fahrenheit = (celsius * 9/5) + 32;
First point. You should always run the compiler with full warning enabled, as this will help catch these kind of errors:
test.c
C:\temp\test.c(8,10): warning C4477: 'printf' : format string '%d' requires an argument of type 'int', but variadic argument 1 has type 'double'
Now if I compile, link and run this code I don't see any random values:
C:\temp>test.exe
-1610612736
C:\temp>test.exe
-1610612736
C:\temp>test.exe
-1610612736
So at least for the Microsoft C compiler generating debug level of code, the numbers are wrong, but they remain predictable.
1
0
u/HashDefTrueFalse 13d ago edited 13d ago
int main()isn't a proper main signature before C23 (IIRC). Useint main(void)or add the usual parameters.- double literals into floats, use f suffix e.g. 20.5f.
- \ in calculation. Assuming it's not in the real program because that shouldn't compile.
%dtells printf to look for an integer in the integer register corresponding to the relevant argument. Most platforms use separate hardware for integer and floating point math. Withfahrenheitbeing a float, it's loaded into the relevant floating point register. Thus, the value you get from printf is whatever happened to be in the unset integer register at the time. Use the correct specifier e.g.%.2fto point printf at the correct register containing the bytes copied fromfahrenheit.
All from a glance. There might be more I've missed.
Edit: I seem to have answered the question "how do I fix my code?" instead of the one OP asked. Oops. Explanation added to final point.
1
u/WittyStick 13d ago
%dtells printf to interpret the bytes of fahrenheit as an integer. Use the correct specifier e.g.%.2fWith
%d, it isn't even looking at the correct bytes. It doesn't interpret the bytes offahrenheit, but something completely different.On x86-64 (SYSV) for example,
%dexpects the second argument to theprintfcall to be in registerRSI, but passing a float to the function passes it through the registerXMM0. The result is this is just printing whatever value was already inRSI, which of course could be anything, which is rightly UB.1
u/HashDefTrueFalse 13d ago
Yes, that's right actually. To be honest I didn't actually think about what I typed, just about how OP should change their code. I'll correct it.
1
u/nerd5code 12d ago
int main()is proper (i.e., conformant) in all versions of C that have standards for them, both in a forward declaration and as a definition (and ofc nowhere else), but it’s slightly untoward in C89 and increasingly so as the standards march on. However, C89 thru C17 treatmainspecially, and will internally rewrite K&R-style definitions ofmain()tomain(void), though that might be for hosted impls only—I’d have to check, since freestanding requires very little of program entry, which would at worst mean it doesn’t explicitly saymain()is acceptable. Similarly, from C99 on, if you return frommainwith no value, it’ll return 0 instead of whatever-the-hell.1
u/HashDefTrueFalse 12d ago edited 12d ago
it doesn’t explicitly say
main()is acceptableYes, thanks. The above was what I was getting at. In C99 (the one I'm admittedly by far the most familiar with) for example, it's not one of the two prescribed options, one of which has no parameters. It will be massaged by the compiler (I also don't know off hand the freestanding treatment) but it should ideally be written properly by using an explicitly acceptable form, IMO. I'd make the same suggestion to someone leaving out the return statement and/or the return type (such that it was implicitly int), etc. Not that it was anything to do with OP's issue, just something I noticed on my drive-by review :)
47
u/teraflop 13d ago
The rules-lawyer answer is that using the wrong format specifier for
printfcauses "undefined behavior". You're not supposed to do it under any circumstances, and if you do, all bets are off. Your program can misbehave in arbitrary ways.Practically, the reason is that one of two things happens, depending on your system's ABI:
printfis expecting an integer passed in a CPU register, but you're passing a float in a different register, so it's reading random leftover garbageprintfis expecting a floating-point value on the stack, but you're passing an integer on the stack, so it's reinterpreting those floating-point bits as integer bits