paupino / rust-decimal

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

Scale changes unexpectedly after operations #632

Closed br0kenpixel closed 10 months ago

br0kenpixel commented 10 months ago

I have the following function:

fn calc_temperature(raw: u16) -> Decimal {
    // Formula: ((175.72 * raw) / 65536.0) - 46.85
    let mut value = Decimal::new(raw as i64 * 100, 2);

    value *= dec!(175.72);
    value /= dec!(65536.00);
    value -= dec!(46.85);

    value
}

Example usage:

let result = calc_temperature(63510u16);
println!("{result} {}", result.scale());

Which gives:

123.437738037109375 15

I'm not sure if I missed something in the documentation, or it's a bug, but why does the scale change from 2 to 15?. Since all decimals have a scale of 2, I'd expect the result to also retain this scale. The only solution I found was adding value.rescale(2) before returning value. Is this normal, or a bug?

Tony-Samuels commented 10 months ago

I think it's easiest to show by example: 0.01 * 0.01 = 0.0001; so a multiplication of two numbers of scale 2, needs a scale of 4 0.01 / 100 = 0.0001; so a division of a number needs a scale that's larger by roughly the log_10 of the divisor.

So you have a scale of 2, the multiplication makes it 4, the division by a number with a log_10 of 4 increases it to 15 and there it stays.

Tony-Samuels commented 10 months ago

I suppose the other way to show why it's needed is that the answer has 15 digits after the decimal place, so any smaller scale would have given an incorrect answer.

If you don't care for exact precision (where it can be obtained):

br0kenpixel commented 10 months ago

Thanks! 😀