I'm a bit confused by the way floating point numbers are converted to strings. There is some conversion of data to and from strings involved (using serde_json, but I think this is not relevant). I see that some precision is lost and I'm wondering how I can do this correctly.
Note the difference in the last digit. I certainly expect the result of the first line. My problem is that I'm not able to know the precision I'd have to put in the format string (.1 in this case) beforehand. Other examples that fail to print correctly use more decimal places, this integer is just an example.
What I need is a way to print the f32 values in a way that does not loose precision.
This is not a problem with printing. This is inherent: f32 has 24 bits of mantissa, which means the only integers it can represent exactly are those less than around 16 million in absolute value. You have 67 million, so the number you have is not exactly representable as an f32. So your expectation to print these kinds of numbers exact to 1 decimal digit (i.e., a total of 9 significant digits) is impossible to satisfy.
But why does the print with format {:.1} work then? This is what confuses me most. If 67108872.0 would not fit into a f32, why is it printed correctly in the first line then?
It doesn't "work", you just happened to be lucky. Or, rather, it's not printed exactly. If you try the same code with the value 67108873.0_f32, you will also get 67108872 when you ask the system to print it with excessive precision, and 67108870 when printed with automatic precision. This is because all three values round to the same internal representation in f32, and when you ask for a too high number of significant digits, then it will try to squeeze out more digits, which, however, aren't meaningful.
You're just lucky in that case, since a lot of numbers round to 67108872.
to_string probably rounds to the least integer number of digits of precision. Even though there is a fractional amount of precision left over, it is not reliable to print another digit.
fn main() {
let f0: f32 = 67108872.0;
for i in 0..10 {
let f = f0 + (i as f32);
println!("{} {:.1} {}", i, f, f.to_string());
}
}
0 67108872.0 67108870
1 67108872.0 67108870
2 67108872.0 67108870
3 67108872.0 67108870
4 67108880.0 67108880
5 67108880.0 67108880
6 67108880.0 67108880
7 67108880.0 67108880
8 67108880.0 67108880
9 67108880.0 67108880