-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Float Display with fixed precision output uses inconsistent rounding rules #70336
Comments
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@johnbartholomew I am not able to find out how Rust is internally handling precision Can you please guide me? |
I think the formatting code starts at libcore/fmt/float.rs. Which is a wrapper around libcore/num/flt2dec/. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I came to this issue also exploring formatting capabilities. For me, it is unclear from the documentation what are the rounding rules. For instance, I cannot explain the following example: fn main() {
println!("{:1.3}", 1.2345); // outputs: 1.234
println!("{:1.3}", 1.23451); // outputs: 1.235
} |
@zyrikby That example looks consistent with round-to-even rules. 1.2345 is exactly half way between 1.234 and 1.235, so round-to-even selects 1.234 (ends with an even digit). 1.23451 is closer to 1.235 than it is to 1.234 so it must select 1.235. Edit: However, the input numbers may not be exactly representable in binary floating point, which could also affect the results. I haven't checked in this case. |
@johnbartholomew My point here more is that these numbers are not that you would expect to see. For instance, if I implement, e.g., an analogue of Excel I expect to see normal rounding rules applied. However, I am a novice in Rust development, so I do not have a right to impose my opinion to language developers (I do appreciate their work). Still, I think that in the library documentation the fact that round-to-even rules are applied should be mentioned. |
Floating point rounding rules are one of those things where no matter what you do, it's unintuitive to someone in some cases. Excel has plenty of user confusion from this too, but it's not surprising that Excel chooses round-to-nearest with 5s going up as its default behavior. Programming languages often favor round-to-even because it reduces the biases introduced by rounding every number in a data set, but no Excel user would ever expect that and Excel expressions are more likely to be rounding a single value or only a few values than a large data set. Regardless, https://doc.rust-lang.org/std/fmt/#precision should definitely specify the rounding behavior. I simply don't know if Rust is doing round-to-even or round-to-zero or something else. |
This also affects fn main() {
for f in -5..5 {
println!("{0} -> {0:.0}", f as f64 + 0.5f64);
}
} gives
|
Triage: Hi, are you still working on this issue @ayushmishra2005? |
@Alexendoo No, I didn't get chance. Please go head and fix. |
@rustbot release-assignment |
Presumably this is also a problem with rounding in the fn main() {
println!("{}, {}, {}", !0u32, !0u32 as f32, (!0u32 as f32) as u64);
}
Why do we see the middle value (2^32 + 4)? Edit: this is just unnecessary rounding during display (code):
|
I saw the same strange behaviour of Display with f64, and made a comparison between Display::fmt and a trivial rounding function. Display was wrong 50% the time in my small test, and The code is here. Here is the output, with comments where Display is wrong.
It's strange that perfectly round values like 0.25 or 0.125, which code well in IEEE-754, are not correctly rounded by |
Since I already had a routine that rounds a number by processing its string representation, I quickly used it to detect erroneously rounded values in a simple test on more values. Maybe not as good as using a crate like I found out the ratio of mismatches was 5 / 22 so about 25%, for different number of fractional digits and only testing values ending by 4 or 5, so 0.4, 0.5 using The ratio remains the same when I check more values by scanning more precisions (same ratio scanning The code can be found here, if that's any help. EDIT: added round-to-even check. If I'm only testing 0.*5 values at all depths, about 50% of the values formatted are not correctly rounded, with round-to-even or round-away-from-zero (the rates are pretty close but not identical) |
I'm not sure that matching the output of But I think having a consistent rounding mode in the formatter is a desirable property to have, and it looks like Rust's current situation is mostly round-to-even, regardless of what |
I think I've shown it was not a good solution indeed, since |
… r=scottmcm Fix inconsistent rounding of 0.5 when formatted to 0 decimal places As described in rust-lang#70336, when displaying values to zero decimal places the value of 0.5 is rounded to 1, which is inconsistent with the display of other half-integer values which round to even. From testing the flt2dec implementation, it looks like this comes down to the condition in the fixed-width Dragon implementation where an empty buffer is treated as a case to apply rounding up. I believe the change below fixes it and updates only the relevant tests. Nevertheless I am aware this is very much a core piece of functionality, so please take a very careful look to make sure I haven't missed anything. I hope this change does not break anything in the wider ecosystem as having a consistent rounding behaviour in floating point formatting is in my opinion a useful feature to have. Resolves rust-lang#70336
docs: mention round-to-even in precision formatting _Note_: Not quite sure exactly how to format this documentation. Mentions round-to-even usage in precision formatting. (should this also be mentioned in `f64::round`?) From rust-lang#70336
Rollup merge of rust-lang#120967 - LeoDog896:master, r=cuviper docs: mention round-to-even in precision formatting _Note_: Not quite sure exactly how to format this documentation. Mentions round-to-even usage in precision formatting. (should this also be mentioned in `f64::round`?) From rust-lang#70336
Rust's f32 formatting seems to follow inconsistent rounding rules when rounding to a fixed output precision.
Output (Playground):
I get this output on Linux x86-64 with both rustc 1.42.0 (b8cedc0 2020-03-09) and rustc 1.44.0-nightly (f509b26 2020-03-18). I have not tested on other systems, and have not yet tested with f64.
Most inputs are rounded following the rule of rounding half to the nearest integer, but 0.5 and -0.5 are rounded away from zero instead.
When rounding 0.5 to an integer both 0 or 1 are correct outputs, so this may not be a bug. However it seems strange that the round-half-to-even rule should be followed for 1.5, 2.5, and up while a different rule is followed for 0.5.
By comparison, my Python and C versions followed round-half-to-even consistently (rounding 0.5 to 0), though I don't know that they provide any guarantee to follow that rule.
Cross-reference: I originally posted about this on the user forum. Rounding mode for fixed precision float Display format?
The text was updated successfully, but these errors were encountered: