【问题标题】:Compute `n * 10^p` as accurately as `f64::from_str` does?像 `f64::from_str` 一样准确地计算 `n * 10^p`?
【发布时间】:2021-04-03 23:30:34
【问题描述】:

我有两个值:n: f64p: i32,我需要计算 n * 10^p

我尝试了两种方法:

  1. 使用乘法和f64::powi
  2. 使用format!()f64::from_str

后者更准确(见下面的输出)但显然效率低下。有没有办法在不经过字符串转换的情况下获得相同的准确性?这是我的代码:

fn main() {
    let f1 = |n: f64, e: i32| n * 10f64.powi(e);
    let f2 = |n: f64, e: i32| format!("{}e{}", n, e).parse::<f64>().unwrap();
    for &n in &[1.1, 2.2, 3.3, 4.4] {
        for &e in &[-2, 2] {
            println!("{} {}", f1(n, e), f2(n, e));
        }
    }
}

输出:

0.011000000000000001 0.011
110.00000000000001 110
0.022000000000000002 0.022
220.00000000000003 220
0.033 0.033
330 330
0.044000000000000004 0.044
440.00000000000006 440

Playground

【问题讨论】:

  • 重复乘以 10 而不是求幂再乘可以解决正幂的准确性问题,但这不适用于负幂。
  • format! 不是更准确,而是更少!您在这里看到的是在 format! → from_str 方法中舍入的结果,再加上浮点数无法存储 10 的精确幂,而不是 2 的幂。碰巧的是,对于那些特定的数字,舍入给出了更好的结果结果。
  • 在 Rust 的标准库中阅读 the dec2flt module documentation 非常有趣。 TL;DR:字符串被解析成(sign, decimal_int, exp_int) 三元组,但这是简单的部分,然后通过一堆花哨的数值近似算法运行它,直到结果从另一端出来。这比我预期的要复杂得多。
  • @mcarton 好吧,这取决于。对于实际上非常适合十进制数的浮点数(例如这些),format!() 实际上是准确的,因为这种转换正是它的含义:将十进制数转换为浮点数(例如“1.1e2 ”)。但是,对于任意浮点数,使用 from_str 然后使用 format!() 会损失大量准确度。查看@pretzelhammer 链接的dec2flt 模块。
  • 根据您需要进行的其他计算,最好不要选择 f64 作为基本类型。如果您决定使用以 10 为基础实现的浮点类型,例如 decimal crate,您可能会更好。也就是说,d128::scaleb 比您的 f64 mulpowi 调用慢一个数量级,但仍比 f64 字符串解析快一个数量级。但是,它不会丢失您似乎关心的域中的精度,即可以用十进制基数精确表示的数字。

标签: rust


【解决方案1】:

一如既往,它有一个板条箱: rust_decimal 它不完全是您想要的,但它为您的任务增加了所需的精度,而不会采用格式化的方式。 不妨试一试,可惜不能在操场上使用。

【讨论】:

    猜你喜欢
    • 2014-12-08
    • 1970-01-01
    • 1970-01-01
    • 2014-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-10
    • 2011-11-27
    相关资源
    最近更新 更多