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

替代措辞 :何时将Double.MIN_VALUE添加到Java中的double 不会导致不同的Double值? (见Jon Skeet的评论如下)

这个关于Java中最小Double值的问题有一些答案在我看来是等价的。 Jon Skeet的回答无疑是有效的,但他的解释并没有让我相信它与理查德的 答案有什么不同。

Jon的回答使用以下内容:

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

理查兹回答提到了Double.MIN_VALUE的JavaDoc

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

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

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

将Double.MIN_VALUE添加到double值和增加表示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); 

OUTPUT:

 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 

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

就像Jon说的那样:

“如果你试图给一个非常大的数字添加一个非常小的数字,那么差异可能很小,以至于最接近的结果与原始结果相同。”

例如:

 // 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会产生新的位模式,因此会导致不等的加倍。