为什么编译器解析源文件中的浮点数与运行时不同?

Why does the compiler parse a floating point number in a source file differently than at runtime?

我最近一直在做一些 Rust 项目来学习这门语言并找点乐子。我正在编写类似于 libconfig in Rust, using the peg crate 的东西来生成我的解析器。

在过去的一个小时里,我一直在与一个奇怪的错误作斗争,在这个错误中,从示例配置文件中解析的某些值与预期值不相等。

最终,我将错误缩小为:

fn main() {
    let my_flt = "10.4e-5".parse::<f32>().unwrap();
    let other_flt = 10.4e-5;
    println!("{} == {} -> {}", my_flt, other_flt, my_flt == other_flt);
}

令人惊讶的是,这会打印出:

0.000104 == 0.000104 -> false

See it in the playground

现在,我知道这 与臭名昭著的旧浮点精度问题有关。我知道即使两个浮点数在打印时看起来可能相同,但由于各种原因它们可以进行不同的比较,但我猜想从 parse::<f32>("X") 获取浮点数等同于显式声明浮点数并将其初始化为 X。显然,我错了,但为什么?

毕竟,如果我将一个浮点数声明并初始化为 X,在内部,编译器在生成最终可执行文件时必须执行与 parse() 相同的工作。

为什么编译器解析源文件中的浮点数X与运行时parse::<f32>()函数的方式不同?这不应该是一致的吗?我好不容易才结束这一切!

请注意,OP 的特定示例不再失败(在 Rust 1.31.0 中测试)


标准库和 rustc 词法分析器解析浮点值的方式不同known issue

标准库最终调用 from_str_radix, and you can see the implementation there. I'm not sure exactly where the compilers version of parsing floating-pint literals takes place, but this comment 表明它利用了 LLVM:

The compiler uses LLVM to parse literals and we can’t quite depend on LLVM for our standard library.