当前位置: 首页 > 知识库问答 >
问题:

为什么Rust只使用16位有效数字进行f64相等性检查?

米俊晤
2023-03-14

我有以下生锈代码:

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?
######################################################################

共有2个答案

萧晔
2023-03-14

您首先假设传递所有这些双字面值3.14159265358979323846264338327950288_f643.141592653589793_f643.14159265358979_f64实际上会将这些精确值分配给变量。这种假设是不正确的。

尽管rust源代码的作者使用数学常数的前36位实际数字来定义f64::PI,但使用IEEE 754浮点格式存储的实际64位值是不同的。根据在线转换器,最接近的IEEE 754 64位浮点值将是0x400921FB542D18,当转换回十进制时,可以使用数字3.1415926535897931159979634685进行近似。当您将IEEE 754值0x400921FB5442D18转换为十进制数时,会得到相同的值。

换句话说:

What we wanted to store: 3.14159265358979323846264338327950288 
What is actually stored: 3.14159265358979311599796346854...

也许更简单的可视化方法是想象一个虚构的数据类型,它可以存储从0到1的正实数,并在内部使用字符串(一个字符数组)表示,最大长度为12个字符。所以,你使用这个奇怪的96位类型,创建5个变量:

strdouble A = 0.333333;       // internally stored as x = { .raw = "0.333333000" }
strdouble B = 0.333333333;    // internally stored as x = { .raw = "0.333333333" }
strdouble C = 0.333333333333; // internally stored as x = { .raw = "0.333333333" }
strdouble D = 0.333333333444; // internally stored as x = { .raw = "0.333333333" }
strdouble E = 0.333333333555; // internally stored as x = { .raw = "0.333333334" }

您可以看到BCD将是相等的,尽管传递给编译器的文字值是完全不同的。您还可以看到算术(1/3 1/3 1/3)返回0.999999999而不是1,因为根本没有方法来表示最后一个原始数字以外的任何精度。

凌征
2023-03-14

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

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

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

 类似资料:
  • 问题内容: 我开始于: 然后尝试: 最终: 从那以后我发现: 因此,我已经解决了最初的问题(有点),但是为什么数组不能互相匹配? 问题答案: Javascript数组是对象,您不能简单地使用相等运算符来了解那些对象的 内容 是否相同。如果两个物体实际上是完全一样的情况下(如平等运营商将只测试,作品和太)。 如果您需要检查两个数组是否相等,我建议只遍历两个数组并验证所有元素具有相同的值(并且两个数组

  • 虽然Rust中的所有整数类型都实现了强调总排序的,但浮点类型只实现了。这意味着可能存在无法比较的浮点值。这似乎很难理解,因为浮点数可以被认为是实数的近似值,实数碰巧是一个完全有序的集合。即使加上正无穷大和负无穷大,也会使实数集保持完全有序。为什么在铁锈这个奇怪的选择? 这种限制意味着通用排序/搜索算法只能假设数字的部分排序。IEEE 754标准似乎提供了一个总排序谓词。 NaN在通用代码中是个大问

  • Rust的枚举是代数数据类型。据我所知,这似乎包含了struct是什么。struct有什么不同之处需要保留它?

  • 如果变量值的数据类型很短,且数字为1,是否将使用16位?

  • 问题内容: 最近,我阅读了很多有关Unicode代码点的信息,以及它们随着时间的演变,并确保我也阅读了http://www.joelonsoftware.com/articles/Unicode.html。 但是我找不到真正的原因是Java为什么将UTF-16用作字符。 例如,如果我的字符串包含1024个ASCII范围的字母。这意味着等于2KB的字符串内存,它将以任何方式消耗。 因此,如果Java

  • 我正在制作一个类似游戏24的程序。 我需要检查用户的输入(一个使用程序提供的四个随机数的数学方程),看看用户给出的数字是否只有一个数字。 例如: 程序吐出6 4 9 5 用户可以输入9 5 6 4(等于24)。 但是我不希望他们能够做任何像95-64这样的事情(不对,只是一个例子)来制造24。 我如何检查等式中的数字是否只有一位数? /***接受用户的数学问题。*然后...*确保输入的数学问题遵循