【问题标题】:Strange behavior of float point浮点数的奇怪行为
【发布时间】:2019-07-27 12:19:21
【问题描述】:

我理解 random.Float() 带回一个介于 0.0 到 0.999 之间的浮点数...

但由于未知原因,几乎相同的代码会返回 2 个不同的答案。

我想了解为什么 t1 可以随机 1.0 而 t2 不能。

我试图检查类似的问题,但找不到类似的东西

    Random rand = new Random();
    for (int i = 0; i < 1000000000; i++) {
        float t1 = 0.9f + rand.nextFloat() * 0.1f;
        float t2 = 0.1f + rand.nextFloat() * 0.9f;
        if (t1 == 1.0f) {
            System.out.println("t1 " + t1);
        }
        if (t2 == 1.0f) {
            System.out.println("t2 " + t2);
        }
    }

没有错误消息我只是不明白为什么 t1 有时会带回 1.0 数字而 t2 不会。

编辑: t1 生成 0.999 的最大值 ... *0.1 即 0.0999 ... + 0.9 = 0.9999 ... t2 生成的最大值为 0.999... *0.9 即 0.8999... + 0.1 = 0.9999... 两者应该是一样的吧?

【问题讨论】:

    标签: java floating-point


    【解决方案1】:

    差异的主要原因是 .9 + x •.1 大于 .1 + x • .9 对于任何 x em> Random.nextFloat 返回小于 1 的值。对于它返回的最大值,前一个表达式非常接近 1,以至于将其四舍五入到 float 产生 1,但后一个表达式离 1 更远,并且将其四舍五入到 float 会产生低于 1 的下一个 float

    The largest value that Random.nextFloat returns is Random.nextfloat 16777215/16777216.M 为这个最大值,令 d = 1 - M = 1/16777216。

    如果我们用实数计算t1,它的最大值将是0.9 + M•.1。 = .9 + (1−d)•.1 = 1−.1•d。同样,t2 将是 .1 + M•.9 = .1 + (1-d)•.9 = 1-.9•d .

    现在我们可以很容易地看到t1 的最大值(如果用实数计算)与 1 相差 0.1•d,但 t2 的最大值为 0.9•d 远离 1。

    Random.nextFloat 返回的最大值为 16777215/16777216 的原因是它最大的 float 小于 1。float 格式的精度使得可表示值之间的步长为 1 1/16777216。这意味着这个邻域中两个可表示的值是16777215/16777216和1,d是它们之间的距离。我们上面的计算表明,t1 的最大值距离 1 仅 0.1•d。因此,当它四舍五入到 float 时,1 是最接近的 float

    相比之下,t2 的最大值距离 1 仅 0.9•d。这意味着它距离 16777215/16777216 仅 0.1•d .所以,当它四舍五入到float时,16777215/16777216是最接近的float

    那是使用实数算术。下面,我将展示浮点运算。它有四舍五入的错误,但结果证明这些错误足够小,不会改变结果。

    Java 中,源文本.1f.9f 被转换为float,得到0.100000001490116119384765625 和0.89999997615814208984375。表达式中的算术使用double 执行,然后将结果四舍五入为float 以分配给t1t2

    t1可以计算出的最大值:

    • 替换nextFloat 的最大返回值得到0.9f + 16777215./16777216 * 0.1f;
    • double 算术中计算得出 0.999999971687793642871611154987476766109466552734375。
    • 转换为 float 得到 1,因为 double 的值介于 0.999999940395355224609375(下一个较低的 float 值)和 1(下一个较高的值)之间,并且与前者相比,它更接近后者。

    t2可以计算出的最大值:

    • 代入nextFloat 的最大返回值得到0.1f + 16777215./16777216 * 0.9f;
    • double 算术中计算得出 0.99999992400407933246242464520037174224853515625。
    • 转换为 float 得到 0.999999940395355224609375。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-27
      • 1970-01-01
      相关资源
      最近更新 更多