Math.pow(2,63) - 1 == Math.pow(2,63) - 512为TRUE

时间:2014-01-04 16:30:31

标签: java double

我认为这很有趣:

System.out.println(  (long)(Math.pow(2,63) - 1) == Long.MAX_VALUE); // true
System.out.println(  (long)(Math.pow(2,63) - 5) == Long.MAX_VALUE); // true 
System.out.println(  (long)(Math.pow(2,63) - 512) == Long.MAX_VALUE); // true
System.out.println(  (long)(Math.pow(2,63) - 513) == Long.MAX_VALUE); // false

最后三行应打印false,但实际上只有最后一行,我减去513给出了正确答案。

为什么512/513是不准确的突破点?我唯一能做到的就是512就是一千字节的一半。


我知道造成这种情况的原因是double不准确,这源于Math.pow(int, int)返回double

我用java.math.BigInteger尝试了这个,这就是我得到的:

BigInteger b = new BigInteger("2");

System.out.println(  (b.pow(63).longValue() - 1) == Long.MAX_VALUE  ); // true
System.out.println(  (b.pow(63).longValue() - 5) == Long.MAX_VALUE  ); // false
System.out.println(  (b.pow(63).longValue() - 512) == Long.MAX_VALUE  ); // false
System.out.println(  (b.pow(63).longValue() - 513) == Long.MAX_VALUE  ); // false

2 个答案:

答案 0 :(得分:7)

这是浮点舍入的问题。 double实际上有53位有效数,因此在2 ^ 63范围内,连续的双精度数除以1024. 512是你可以减去并且仍然向上舍入的最多。减去513已经超过下一次双下降的一半,然后向下舍入。

该计划证明了这个问题:

import java.math.BigDecimal;

public class Test
{
  public static void main(String[] args) {
    double maxValue = (double)Long.MAX_VALUE;
    System.out.println(new BigDecimal(maxValue));
    double nextDown = Math.nextAfter(maxValue, 0);
    System.out.println(new BigDecimal(nextDown));
    System.out.println(maxValue-nextDown);
  }
}

打印:

9223372036854775808
9223372036854774784
1024.0

显示Long.MAX_VALUE的双倍当量与零方向的下一个双精度之间的1024差距。

答案 1 :(得分:1)

double的内部表示使用52位来存储有效数字。这意味着由double表示的任何数字都具有类似

的形式
1.ddddddddddddddddddddddd x 2 ^ n
  ^^ 52 zeroes or ones ^^

当您将某个其他形式的数字转换为double时,您实际上会获得最适合此格式的数字。

现在,数字2 ^ 63 = 9223372036854775808具有精确的double表示形式,因为它可以存储为

1.00000000000000000000000 x 2 ^ 63
  ^^ 52 zeroes         ^^

并且号码2 ^ 63 - 2 ^ 10 = 9223372036854774784也具有精确的double表示。它是

1.11111111111111111111111 x 2 ^ 62
  ^^ 52 ones           ^^

显然,这两者之间没有数字可以用double完全表示。

现在,

  • Long.MAX_VALUE = 2 ^ 63 - 1 = 9223372036854775807。可以用双精确表示的最接近的数字是2 ^ 63 = 9223372036854775808
  • 2 ^ 63 - 5 = 9223372036854775803。同样,可以用双精确表示的最接近的数字是2 ^ 63 = 9223372036854775808
  • 2 ^ 63 - 512 = 9223372036854775296。这恰好在92233720368547758089223372036854774784之间。因此,Java必须选择这两个值中的一个,以将9223372036854775296表示为double。它向上舍入 - 也就是说,它选择更高的数字。
  • 2 ^ 63 - 513 = 9223372036854775295。可以用双精确表示的最接近的数字是2 ^ 63 - 2 ^ 10 = 9223372036854774784。最后,我们有一个与double具有不同 Long.MAX_VALUE代表的数字。