我认为这很有趣:
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
答案 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
。这恰好在9223372036854775808
和9223372036854774784
之间。因此,Java必须选择这两个值中的一个,以将9223372036854775296
表示为double
。它向上舍入 - 也就是说,它选择更高的数字。2 ^ 63 - 513 = 9223372036854775295
。可以用双精确表示的最接近的数字是2 ^ 63 - 2 ^ 10 = 9223372036854774784
。最后,我们有一个与double
具有不同 Long.MAX_VALUE
代表的数字。