【问题标题】:Are these two approaches to the smallest Double values in Java equivalent?Java 中最小 Double 值的这两种方法是否等效?
【发布时间】:2010-09-07 16:20:08
【问题描述】:

替代措辞:什么时候将 Double.MIN_VALUE 添加到 Java 中的 double会导致不同的 Double 值? (请参阅下面 Jon Skeet 的评论)

SO question 关于 Java 中的最小 Double 值有一些在我看来是等效的答案。 Jon Skeetanswer 无疑是有效的,但他的解释并没有让我相信它与 Richard's answer 有何不同。

乔恩的回答使用以下内容:

double d = // your existing value;
long bits = Double.doubleToLongBits(d);
bits++;
d = Double.longBitsToDouble();

Richards 的回答提到了 Double.MIN_VALUE 的 JavaDoc

保持最小的常数 double 类型的正非零值, 2-1074。它等于十六进制 浮点字面量 0x0.0000000000001P-1022 也等于 到Double.longBitsToDouble(0x1L)

我的问题是,Double.logBitsToDouble(0x1L) 与 Jon 的 bits++; 有何不同?

Jon 的评论侧重于基本的浮点问题。

添加之间有区别 Double.MIN_VALUE 为双精度值, 并增加位模式 代表双。他们是 完全不同的操作,由于 浮点数的方式 被存储。如果你尝试添加一个非常 很小的数字到很大的数字, 差异可能很小 最接近的结果与 原本的。将 1 加到当前 然而,位模式总是 改变相应的浮动 点值,尽可能小 在该比例下可见的值。

我认为 Jon 增加一个长的“bits++”的方法与添加 Double.MIN_VALUE 没有任何区别。他们什么时候会产生不同的结果?

我编写了以下代码来测试差异。也许有人可以提供更多/更好的样本双数或使用循环来查找存在差异的数字。

    double d = 3.14159269123456789; // sample double
    long bits = Double.doubleToLongBits(d);
    long bitsBefore = bits;
    bits++;
    long bitsAfter = bits;
    long bitsDiff = bitsAfter - bitsBefore;
    long bitsMinValue = Double.doubleToLongBits(Double.MIN_VALUE);
    long bitsSmallValue = Double.doubleToLongBits(Double.longBitsToDouble(0x1L));

    if (bitsMinValue == bitsSmallValue)
    {
        System.out.println("Double.doubleToLongBits(0x1L) is same as Double.doubleToLongBits(Double.MIN_VALUE)");           
    }        

    if (bitsDiff == bitsMinValue)
    {
        System.out.println("bits++ increments the same amount as Double.MIN_VALUE");
    }

    if (bitsDiff == bitsMinValue)
    {
        d = d + Double.MIN_VALUE;
        System.out.println("Using Double.MIN_VALUE");
    }
    else
    {
        d = Double.longBitsToDouble(bits);
        System.out.println("Using doubleToLongBits/bits++");
    }

    System.out.println("bits before: " + bitsBefore);   
    System.out.println("bits after: " + bitsAfter);
    System.out.println("bits diff: " + bitsDiff);   
    System.out.println("bits Min value: " + bitsMinValue);
    System.out.println("bits Small value: " + bitsSmallValue);  

输出:

Double.doubleToLongBits(Double.longBitsToDouble(0x1L)) is same as Double.doubleToLongBits(Double.MIN_VALUE)
bits++ increments the same amount as Double.MIN_VALUE
Using doubleToLongBits/bits++
bits before: 4614256656636814345
bits after: 4614256656636814346
bits diff: 1
bits Min value: 1
bits Small value: 1

【问题讨论】:

    标签: java floating-point


    【解决方案1】:

    好的,让我们这样想象,坚持使用十进制数字。假设您有一个浮点类型,它允许您表示 5 个十进制数字,以及一个介于 0 和 3 之间的数字作为指数,将结果乘以 1、10、100 或 1000。

    所以最小的非零值只是 1(即尾数=00001,指数=0)。最大值为 99999000(尾数=99999,指数=3)。

    现在,将 1 添加到 50000000 会发生什么?你不能表示 50000001...500000000 之后的下一个可表示的数字是 50001000。因此,如果你尝试将它们加在一起,结果将是最接近“真实”结果的值——仍然是 500000000。那就是比如将Double.MIN_VALUE 添加到一个大的double

    我的版本(转换为位,递增然后再转换回来)就像取那个 50000000,分成尾数和指数 (m=50000, e=3) 然后将其递增到最小的数量,到 (m=50001, e =3) 然后重新组装成50001000。

    你知道它们有什么不同吗?


    下面是一个具体的例子:

    public class Test{
        public static void main(String[] args) {
            double before = 100000000000000d;
            double after = before + Double.MIN_VALUE;
            System.out.println(before == after);
    
            long bits = Double.doubleToLongBits(before);
            bits++;
            double afterBits = Double.longBitsToDouble(bits);
            System.out.println(before == afterBits);
            System.out.println(afterBits - before);
        }
    }
    

    这会尝试大量的两种方法。输出是:

    true
    false
    0.015625
    

    遍历输出,意思是:

    • 添加Double.MIN_VALUE没有任何效果
    • 增加位确实有效果
    • afterBitsbefore 之间的差异是 0.015625,比 Double.MIN_VALUE很多。难怪简单的加法没有效果!

    【讨论】:

      【解决方案2】:

      正如乔恩所说:

      "如果你尝试添加一点点 数字到一个非常大的数字, 差异可能很小,以至于 最接近的结果与 原创。”

      例如:

      // True:
      (Double.MAX_VALUE + Double.MIN_VALUE) == Double.MAX_VALUE
      // False:
      Double.longBitsToDouble(Double.doubleToLongBits(Double.MAX_VALUE) + 1) == Double.MAX_VALUE)
      

      MIN_VALUE 是可表示的最小正双精度数,但这当然并不意味着将其添加到任意双精度数会导致不相等。

      相比之下,将 1 添加到底层位会导致新的位模式,因此会导致不相等的双精度数。

      【讨论】:

        猜你喜欢
        • 2021-08-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-24
        • 2014-09-29
        • 2016-11-08
        • 2011-03-20
        相关资源
        最近更新 更多