rust - 为什么 Rust 只使用 16 位有效数字进行 f64 相等检查?
问题描述
我有以下锈代码:
use std::f64::consts as f64;
fn main() {
println!("Checking f64 PI...");
// f64::PI definition: https://github.com/rust-lang/rust/blob/e1fc9ff4a794fb069d670dded1a66f05c86f3555/library/core/src/num/f64.rs#L240
println!("Definition: pub const PI: f64 = 3.14159265358979323846264338327950288_f64;");
println!("Print it: {:.35}", f64::PI);
println!("Different after 16 significant digits ----------| ");
println!("##############################################################################");
println!("Question 1: Why do the digits differ after 16 significant digits when printed?");
println!("##############################################################################");
println!("PERFORM ASSERTIONS...");
assert_eq!(f64::PI, 3.14159265358979323846264338327950288_f64); // 36 significant digits definition
assert_eq!(f64::PI, 3.141592653589793_f64); // 16 significant digits (less then the 36 in definition)
// compares up to here -------------|
assert_eq!(f64::PI, 3.14159265358979300000000000000000000_f64); // 36 significant digits (16 used in equality comparison)
assert_ne!(f64::PI, 3.14159265358979_f64); // 15 significant digits (not equal)
println!("PERFORM EQUALITY CHECK...");
if 3.14159265358979323846264338327950288_f64 == 3.14159265358979300000000000000000000_f64 {
println!("BAD: floats considered equal even when they differ past 16 significant digits");
println!("######################################################################");
println!("Question 2: Why does equality checking use only 16 significant digits?");
println!("They are defined using 36 significant digits so why can't we perform");
println!("an equality check with this accuracy?");
println!("######################################################################");
} else {
println!("GOOD: floats considered different when they differ past 16 significant digits");
println!("NOTE: This block won't execute :(");
}
}
我知道浮点运算可能很棘手,但想知道这种棘手是否也会影响 f64 的打印和执行相等性检查。以下是上述代码的输出:
Checking f64 PI...
Definition: pub const PI: f64 = 3.14159265358979323846264338327950288_f64;
Print it: 3.14159265358979311599796346854418516
Different after 16 significant digits ----------|
##############################################################################
Question 1: Why do the digits differ after 16 significant digits when printed?
##############################################################################
PERFORM ASSERTIONS...
PERFORM EQUALITY CHECK...
BAD: floats considered equal even when they differ past 16 significant digits
######################################################################
Question 2: Why does equality checking use only 16 significant digits?
They are defined using 36 significant digits so why can't we perform
an equality check with this accuracy?
######################################################################
解决方案
f64
顾名思义,An以 64 位存储。在这个固定数量的存储空间中,我们只能对固定数量的数字进行编码(具体来说,其中 52 个位专用于有效位)。如果您在浮点文字中使用更多数字,则存储在f64
变量中的数字将四舍五入为可用位数表示的最接近的数字。因为f64
这意味着我们总是可以精确地表示 15 位十进制数字,有时是 16。这解释了为什么有时即使您在源代码中使用不同的浮点文字,数字看起来也相等:这是因为在四舍五入到最接近的可表示数字后,它们是相同的.
打印不同数字的原因是相同的。该数字在存储时四舍五入到最接近的可表示数字,并在打印时再次转换回十进制。额外的数字来源于二进制到十进制的转换,但小数点后 15 或 16 位之后的数字大多没有意义——它们不携带任何关于所表示数字的额外信息。
请注意,这些都不是 Rust 特有的。大多数现代编程语言使用 IEEE 754-1985 标准来表示浮点数,因此它们的行为相同。如果你想要任意精度的算术,你通常需要使用一些库,例如rug crate。
推荐阅读
- c++ - 当我们可以声明,而不是定义,但仍然在 C++ 中使用这些函数时
- typescript - 解决外面的承诺
- acumatica - 如何解决“ClientSideAppsHelper.RenderScriptConfiguration 已过时”
- angular - Angular:Service Observable 不会在组件中触发
- jquery - 按下向下箭头键时无法将类添加到自动完成 ul il 列表并且无法专注于 li 列表元素(不使用 jquery UI)
- angular - angular 8 Cordova 插件承诺
- sitefinity - 自定义小部件设计器以模态屏幕外呈现
- powerbi - Dax - 基于过滤器参数的动态属性值
- javascript - JS通过字符串eval更新数组
- javascript - 为什么`Object.keys/values/entries`返回数组而`Array ...`返回迭代器