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

Java Math.pow(2,63)-1==Math.pow(2,63)-512为真,java,double,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

我觉得这很有趣:

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是半千字节


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

我试过这个,我得到的是:

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

这是一个浮点舍入的问题。一个double实际上有53位有效位,因此在2^63范围内,连续的double被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_值的双精度等效值与零方向上的下一个双精度值之间的1024间隙。

双精度的内部表示使用52位存储有效位。这意味着由双精度表示的任何数字的形式如下

1.ddddddddddddddddddddddd x 2 ^ n
  ^^ 52 zeroes or ones ^^
当您将其他形式的数字转换为双精度数字时,您实际上会得到适合此格式的最接近的数字

现在,数字
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_值=2^63-1=9223372036854775807
    。与此最接近的可以用双精度表示的数字是
    2^63=9223372036854775808
  • 2^63-5=9223372036854775803
    。同样,可以用双精度表示的与此最接近的数字是
    2^63=9223372036854775808
  • 2^63-512=9223372036854775296
    。这正好介于
    9223372036854775808
    9223372036854774784
    之间。因此Java必须从这两个值中选择一个,将
    9223372036854775296
    表示为
    double
    。它四舍五入-也就是说,它选择更高的数字
  • 2^63-513=9223372036854775295
    。与此最接近的可以用双精度表示的数字是
    2^63-2^10=9223372036854774784
    。最后,我们有一个与
    Long.MAX_值
    不同的
    表示的数字

更简单的示例:
double d=Long.MAX\u值;System.out.println(d-512==d);系统输出println(d-513==d)