See also: the 902.1 incident of 2020.
Once again, oddness from floating point values took me down a rabbit hole trying to understand why something was not working as I expected.
Earlier, I had stumbled upon one of the magic values that a 32-bit floating point value cannot represent in C. Instead of 902.1, a float will give you 902.099976… Close, but it caused me issues due to how we were doing some math conversions.
float value = 902.1;
printf ("value = %f\n", value);
To work around this, I switched these values to double precision floating point values and now 902.1 shows up as 902.1:
double value = 902.1;
printf ("value = %f\n", value);
That example will indeed show 902.100000.
This extra precision ended up causing a different issue. Consider this simple code, which took a value in kilowatts and converted it to watts, then converted that to a signed integer.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(int argc, char **argv)
{
double kw = 64.60;
double watts = kw * 1000;
printf ("kw : %f\n", kw);
printf ("watts: %f\n", watts);
printf ("int32: %d\n", (int32_t)watts);
return EXIT_SUCCESS;
}
That looks simple enough, but the output shows it is not:
kw : 64.600000 watts: 64600.000000 int32: 64599
Er… what? 64.6 multiplied by 1000 displayed as 64600.00000 so that all looks good, but when converted to a signed 32-bit integer, it turned in to 64599. “Oh no, not again…”
I was amused that, by converting these values to float instead of double it worked as I expected:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(int argc, char **argv)
{
float kw = 64.60;
float watts = kw * 1000;
printf ("kw : %f\n", kw);
printf ("watts: %f\n", watts);
printf ("int32: %d\n", (int32_t)watts);
return EXIT_SUCCESS;
}
kw : 64.599998 watts: 64600.000000 int32: 64600
Apparently, whatever extra precision I was gaining from using double in this case was adding enough extra precision to throw off the conversion to integer.
I don’t know why. But at least I have a workaround.
Until next (floating point problem) time…
Using %f with printf for double seems to be the problem, kind of. Try the first example with %lf instead
https://stackoverflow.com/questions/1786497/sprintf-double-precision-in-c
OK… remember, *printf and *scanf are varargs functions, and the optional parts of varargs get the “usual argument promotions” that go back to K&R 1st edition. In particular, floats get converted to doubles before being put where they go depending on the target processor ABI. %f and %e expect doubles. %lf came with the “long double” type in some revision of ANSI/ISO C, and expects a long double, so that will try to convert part of the stack frame.
If a number can’t be written as m/2^n for some integer m and some non-negative integer n, it can’t be represented in binary floating point. (That’s a necessary condition, not sufficient, because floating point formats have finitely many mantissa bits.) Ten is not a power of two, so you’re out of luck for accurate representation. The C standard says it rounds to the specified number of digits (6, since you didn’t specify), so that kw value will print eight digits. One decimal digit is, VERY roughly, 3.3 bits (1 / 0.30103), so that’s asking for at least 26 bits, but IEEE 754 binary single precision is just 24, even counting the implied 1 for normalized values.
You need to decide how many decimal places you want and tell printf about it.