为什么编译器解析源文件中的浮点数与运行时不同?
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
现在,我知道这 与臭名昭著的旧浮点精度问题有关。我知道即使两个浮点数在打印时看起来可能相同,但由于各种原因它们可以进行不同的比较,但我猜想从 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.
我最近一直在做一些 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
现在,我知道这 与臭名昭著的旧浮点精度问题有关。我知道即使两个浮点数在打印时看起来可能相同,但由于各种原因它们可以进行不同的比较,但我猜想从 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.