首页 > 解决方案 > 为什么 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?
######################################################################

标签: rustfloating-pointprecision

解决方案


f64顾名思义,An以 64 位存储。在这个固定数量的存储空间中,我们只能对固定数量的数字进行编码(具体来说,其中 52 个位专用于有效位)。如果您在浮点文字中使用更多数字,则存储在f64变量中的数字将四舍五入为可用位数表示的最接近的数字。因为f64这意味着我们总是可以精确地表示 15 位十进制数字,有时是 16。这解释了为什么有时即使您在源代码中使用不同的浮点文字,数字看起来也相等:这是因为在四舍五入到最接近的可表示数字后,它们是相同的.

打印不同数字的原因是相同的。该数字在存储时四舍五入到最接近的可表示数字,并在打印时再次转换回十进制。额外的数字来源于二进制到十进制的转换,但小数点后 15 或 16 位之后的数字大多没有意义——它们不携带任何关于所表示数字的额外信息。

请注意,这些都不是 Rust 特有的。大多数现代编程语言使用 IEEE 754-1985 标准来表示浮点数,因此它们的行为相同。如果你想要任意精度的算术,你通常需要使用一些库,例如rug crate


推荐阅读