如何处理 Rust 中不精确的浮点运算结果?

How to deal with inexact floating point arithmetic results in Rust?

如何在 Rust 中处理浮点运算?

For example:

fn main() {
    let vector = vec![1.01_f64, 1.02, 1.03, 1.01, 1.05];

    let difference: Vec<f64> = vector.windows(2).map(|slice| slice[0] - slice[1]).collect();
    println!("{:?}", difference);
}

Returns:

[-0.010000000000000009, -0.010000000000000009, 0.020000000000000018, -0.040000000000000036]

预期输出:

[-0.01, -0.01, 0.02, -0.04]

我了解发生这种情况的原因,但从未解决过。

更新:

post 的出现是因为 Python 等其他语言的结果似乎是准确的。计划是在 Rust 中复制 Python 的方法,但是经过进一步调查,Python、numpy 和 pandas 都以相同的方式处理数字。也就是说,错误仍然存​​在,但并不总是 visible/shown。另一方面,Rust 使这些错误变得显而易见,起初令人困惑。

示例:

l = [1.01, 1.02, 1.03, 1.01, 1.05]

for i in l:
    print('%.18f' % i)

打印:

1.010000000000000009
1.020000000000000018
1.030000000000000027
1.010000000000000009
1.050000000000000044

print(l) 打印:

[1.01, 1.02, 1.03, 1.01, 1.05]

既然你知道为什么会这样,我假设你想要格式化输出。

official docs

Precision

For non-numeric types, this can be considered a "maximum width". If the resulting string is longer than this width, then it is truncated down to this many characters and that truncated value is emitted with proper fill, alignment and width if those parameters are set.

For integral types, this is ignored.

For floating-point types, this indicates how many digits after the decimal point should be printed.

There are three possible ways to specify the desired precision:

  1. An integer .N:

    the integer N itself is the precision.

  2. An integer or name followed by dollar sign .N$:

    use format argument N (which must be a usize) as the precision.

  3. An asterisk .*:

    .* means that this {...} is associated with two format inputs rather than one: the first input holds the usize precision, and the second holds the value to print. Note that in this case, if one uses the format string {<arg>:<spec>.*}, then the <arg> part refers to the value to print, and the precision must come in the input preceding <arg>.

所以在你的情况下,其中一个可以完成工作:

println!("{0:.2?}", difference);
println!("{1:.0$?}", 2, difference);
println!("{:.*?}", 2, difference);
println!("{1:.*?}", 2, difference);
println!("{number:.prec$?}", prec = 2, number = difference);

Playground

但是,如果您想继续使用这种精度,您可以对结果进行四舍五入:

.map(|x| (x * 100.0).round() / 100.0)

Playground


另请参阅: