如何以最小的精度损失检查大于 2 ^ 54 的 u64 数是否可以被 f64 整除?

Str*_*667 3 floating-point precision rust

对于u64小于 2 ^ 54 的数字,可以通过强制转换为 来完成,而不会造成太大的精度损失f64

((6 as f64) % 1.5) < f64::EPSILON
Run Code Online (Sandbox Code Playgroud)

对于较大的数字,将会有显着的精度损失:

1u64 << 63           // 9223372036854775808
(1u64 << 63) as f64  // 9223372036854776000
Run Code Online (Sandbox Code Playgroud)

并且将检查不同数字的可分性。

Context : JSONSchema 的multipleOf关键字实现

问题:检查不符合尾数大小(53)的u64/i64数字的可整性的最有效方法是什么?f64f64::MANTISSA_DIGITS

Eri*_*hil 5

这是一个给定的解决方案,它u是某个整数并且x是一个有限的非零 IEEE-754 binary64 数,我们用它来进行 IEEE-754 算术。x假定代表一个特定数字,如 IEEE-754 所指定,并且x不考虑获取时发生的先前舍入误差。这个答案涉及所涉及的数学,而不是 Rust 语义,因为我不熟悉 Rust。

首先,找出x= F• 2的表示E,其中F是奇数,E是整数。一个简单的方法是:

  • 设置FxE为 0。
  • 虽然F不是整数,乘以F2 并从 中减去 1 E
  • F是偶数时,除以F2 并在 上加 1 E

以上所有操作都可以在 IEEE-754 算法中执行,没有舍入错误。如果 Rust 提供了一种分离浮点数的有效数和指数的方法,类似于 C 的frexp功能,那么将其合并到上面可以提高效率。

现在考虑是否ux= F•2的倍数E。根据定义,当且仅当存在一个整数k使得u= kF• 2 E。我们将看到这是这样的,当且仅当u是 2 的倍数F并且 是 2 的倍数E,并且这些中的每一个都可以被测试。

如果 2E是一个整数(E是非负数)并且这样的 ak存在,则u是 2 的倍数F并且是 2 的倍数E。相反,如果u不是 2 的倍数F或不是 2 的倍数E,则不k存在(通过算术基本定理)。

F必须在请求的整数格式的范围内(最多 53 位),我们假设F可以转换为该格式。然后可以测试uby 的整除性F。如果 2E超过u表示的整数格式的最大值,则u不是 2 的倍数E。否则,E可以将2转换为格式,并可以测试u被 2的整除性E

如果 2E不是整数(E是负数),那么,如果所需的k存在(所以u是 的倍数F),它是 2 的倍数E. 相反,如果k不是 2 的倍数E,则kF• 2E不是整数,因此不能等于u。因此ux当且仅当u的倍数 是 的倍数F