paupino / rust-decimal

Decimal number implementation written in pure Rust suitable for financial and fixed-precision calculations.
https://docs.rs/rust_decimal/
MIT License
972 stars 177 forks source link

To f64 losing accuracy for `100000.000000000000000000` #624

Closed zzhengzhuo closed 9 months ago

zzhengzhuo commented 9 months ago

When 100000.000000000000000000 converting to f64, it will lose accuracy. But for 10000.000000000000000000 and 1000000.000000000000000000, it works well.

The test code is

#[test]
fn test_decimal() {
    let decimal = Decimal::from_str_exact("100000.000000000000000000").unwrap();
    dbg!(f64::try_from(decimal).unwrap());
    let decimal = Decimal::from_str_exact("1000000.000000000000000000").unwrap();
    dbg!(f64::try_from(decimal).unwrap());
    let decimal = Decimal::from_str_exact("10000.000000000000000000").unwrap();
    dbg!(f64::try_from(decimal).unwrap());
}

The result is

image

The rust-decimal version is 1.32.0

paupino commented 9 months ago

This appears to be related to the rounding that is happening during to_f64:

let round_to = 10f64.powi(self.scale() as i32);
Some((value * round_to).round() / round_to)

In this case, we try doing the following:

100000000000000000000000 / 1000000000000000000

This fails in the last case due to an approximation issue. Interestingly enough this doesn't need to be rounded since we're already dealing with an integer due to a zero fractal component so we can just return early. I'll whip together a PR demonstrating this.