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

Rescaling a value with significant scale 28 to scale 29 creates a value with scale 29 and overflows on subtraction #618

Closed david-monroe closed 10 months ago

david-monroe commented 10 months ago
fn main() {
    let mut num = dec!(0.0000000000000000000000000001);
    dbg!(num);
    num.rescale(29);
    dbg!(num);
    dbg!(num.scale());
    dbg!(num - dec!(0.1));
}
[src/main.rs:6] num = 0.0000000000000000000000000001
[src/main.rs:8] num = 0.00000000000000000000000000010
[src/main.rs:9] num.scale() = 29
[src/main.rs:10] num - dec!(0.1) = -9999999999999999999999999990

Note that num.rescale(30) creates a value with scale 30 but does not overflow.

This happens when reading values with large scale from a database.

paupino commented 10 months ago

Thanks for raising this. I performed some analysis and found that (long story short) scale 29 was causing an issue since it was "aligning" to a scale of 29 however reconstructing to a scale of 28. Because a scale of 29 is technically unsupported (except for very small numbers) it made sense to instead force scaling to 28 which effectively solves the issue.

To note, it didn't happen for scale of 30 since the very small number leaked into the high word triggering a full rescale. For scale 28 we didn't see the issue either since it was already within scale.