这两种方法是否与Java中最小的Double值相当?

时间:2010-09-07 16:20:08

标签: java floating-point

替代措辞:什么时候将Double.MIN_VALUE添加到Java中的double 而不是会产生不同的Double值? (见Jon Skeet在下面的评论)

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

Jon的回答使用以下内容:

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

理查兹回答提到Double.MIN_VALUE

的JavaDoc
  

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

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

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

  

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

我没有看到Jon添加Double.MIN_VALUE增加长“bit ++”的方法有什么不同。什么时候会产生不同的结果?

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

    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

2 个答案:

答案 0 :(得分:7)

好的,让我们用这种方式想象它,坚持使用十进制数字。假设您有一个浮点小数点类型,它允许您表示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。难怪简单的添加没有效果!

答案 1 :(得分:3)

正如乔恩所说的那样:

  

“如果你尝试添加一点点   号码到一个非常大的数字,   差异可能很小   最接近的结果与   原“。

例如:

// 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加到基础位会导致新的位模式,从而导致不等的加倍。