首页 > 解决方案 > 如何检查大于 2 ^ 54 的 u64 数是否可被 f64 整除且精度损失最小?

问题描述

对于u64小于 2 ^ 54 的数字,可以通过转换为f64

((6 as f64) % 1.5) < f64::EPSILON

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

1u64 << 63           // 9223372036854775808
(1u64 << 63) as f64  // 9223372036854776000

并且将检查可除性以获得不同的数字。

上下文:JSONSchema 的multipleOf关键字实现

问题:检查不适合尾数大小(即 53)的u64/数字的可除性的最有效方法是什么?i64f64f64::MANTISSA_DIGITS

标签: rustfloating-pointprecision

解决方案


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

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

  • 设置F为0xE0。
  • WhileF不是整数,乘以F2 并从 中减去 1 E
  • 虽然F是偶数,但除以F二并加一E

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

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

如果 2E是一个整数(E非负数)并且k存在这样的 a,则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 -<code>E的倍数。相反,如果k不是 2 −<code>E的倍数,则kF• 2E不是整数,因此它不能等于u。因此ux当且仅当u是 的倍数时,是 的倍数F


推荐阅读