【问题标题】:Check if a number is exactly representable as `f32`检查一个数字是否完全可以表示为 `f32`
【发布时间】:2019-02-20 09:02:10
【问题描述】:

我想知道值 1/256、2/256、3/256、... 254/256 和 255/256 是否可以精确地表示为 f32。现在,聪明的人会思考浮点数是如何工作的并找出答案。但我想在程序中检查一下。我要检查的所有数字都是分数,我控制值(即没有用户输入)。

我是从这个开始的:

for n in 1u8..=255 {
    let f = (n as f32) / 256.0;
    println!("{}", f);
}

但是现在呢?我尝试打印该数字以查看是否有大量重复数字,但这并不总是有效。例如,0.4 不能完全表示:

println!("{}", 0.4);     // prints "0.4"
println!("{:.20}", 0.4); // prints "0.40000000000000002220"

在这里,我们必须手动提高精度才能看到问题。无论如何,查看字符串输出似乎是一个次优的解决方案。

首先我认为f32 上可能有一个方法,但这没有多大意义,不是吗?因为当 f32 已经存在时,没有办法知道它的值是否是预期的。所以我们必须以某种方式找出何时创建浮点值并与“理想化”值进行比较?

有什么方法可以检查一个值是否可以精确地表示为f32

【问题讨论】:

  • 这是一个“几乎总是”有效的测试,尽管它需要改进:你从两个表示不可约分数a/b 的任意大整数开始。如果 b 不是 2 的精确幂(即 b 不是 1,2,4,8,...),那么您的数字将无法准确表示。如果a 大于大约 2^23,那么它将无法精确表示。否则(即如果 a<2^23b 是 2 的精确幂),它很可能是可表示的。但是,我跳过了很多怪癖,将所有内容都考虑在内绝非易事。
  • @Jojonete 这很少值。也许 f32 不是在这种情况下使用的编码。
  • @Darth:为什么值很少?你有一个分子和一个分母。只有分母必须是 2 的幂,而不是分子,然后你才有可能使用指数,所以你几乎有 2^32 个可能的值。
  • @Jojonete 评论的一些额外信息:(1)使用最大公约数(A/B =a/ba=A/gcd(A,B)b=B/gcd(A,B))将A/B 减少为不可约分数。 (2)check if b is a power of two。 (3)你必须摆弄一下指数才能找出b的范围

标签: rust floating-point ieee-754


【解决方案1】:

rug crate 中的 Rational 类型可以精确表示分数。它还实现了PartialEq<f32>,因此您可以直接将精确表示与f32 进行比较,以检查它们是否相等。

for n in 1u8..=255u8 {
    let rat = Rational::from((n, 256));
    let f = (n as f32) / 256.0;
    println!("{}/256 -> {}", n, rat == f);
}

从输出中可以看出,您要测试的数字确实可以精确地表示为f32

要获得更多更有趣的输出,请尝试1 / n

for n in 1u8..=255u8 {
    let rat = Rational::from((1, n));
    let f = 1.0 / (n as f32);
    println!("1/{} -> {}", n, rat == f);
}

这表明只有分母为 2 次方的分数是可精确表示的。

【讨论】:

  • 2 次幂分母是一个规则。其他两条规则是:分子的位数不要太多(否则无法用浮点数表示),分母的幂不要太高(导致浮点数下溢)。如果分母为 1,则分子可以有尾随 0 位,但不能太多(浮点溢出)。看到它在 Squeak Smalltalk source.squeak.org/trunk/Kernel-nice.853.diff 中实现。这里的 float 是 64 位,但这并不重要,并且在 isAnExactFloat 的实现中没有硬编码。
【解决方案2】:

以更高的精度进行您想要的计算(f64 是显而易见且最快的,但也有其他选择:例如f128BigDecimalrug'srationalfloatetc.) ,然后检查结果是否等于自身转换为f32 并返回。

为了举例,假设f64

d.is_finite() && (d as f32) as f64 == d

当然,正如 Jojonete 的评论所指出的那样,即使确切的结果不是,这些计算的结果最终也可能完全可以表示为 f32。因此,您想要的数据类型将取决于计算。例如。对于

1/256、2/256、3/256、... 254/256 和 255/256

rug::rational 肯定是准确的(f64 也是如此,但是你需要“考虑一下浮点数是如何工作的,并找出这种方式”至少一点)。

【讨论】:

  • 这并不总是有效。更高的精度不是精确的精度,因此足够接近 f32 值的数字会产生误报。比如说,5 + 1E-1000 会给出误报。
  • 是的,我应该澄清一下限制(尽管使用BigDecimal 这个例子应该可以工作)。
  • 编辑了答案。
猜你喜欢
  • 1970-01-01
  • 2020-01-18
  • 2021-06-17
  • 2015-03-08
  • 1970-01-01
  • 1970-01-01
  • 2021-09-14
  • 2022-01-17
  • 2019-06-28
相关资源
最近更新 更多