【发布时间】:2015-09-19 08:43:00
【问题描述】:
为什么System.out.println((long)Math.pow(2,63));和System.out.println((long)(Math.pow(2,63)-1));在Java中的输出是一样的?
【问题讨论】:
为什么System.out.println((long)Math.pow(2,63));和System.out.println((long)(Math.pow(2,63)-1));在Java中的输出是一样的?
【问题讨论】:
输出是相同的,因为double 没有足够的位来准确表示 263。
这为您提供最多 17 位十进制数字的精度。另一方面,您计算的值是 9223372036854775808,因此需要 19 位数字才能准确表示。结果,263的实际表示是9223372036854776000:
1的表示尾数相同,而指数为1024为有效值0,即两个数的指数相差63,大于尾数的大小。
当您的号码表示为 double 时,会发生减 1。由于被减数的幅度远大于减数的幅度,所以整个减法运算都被忽略了。
减去更大的数字后,您会得到相同的结果 - 一直到 512,即 29 (demo)。之后,指数的差异将小于 52,因此您将开始得到不同的结果。
【讨论】:
Math.pow( double, double ) 返回一个double 值。
double 在 java 中是一个 64 位 IEEE 754 浮点数。(https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html)
如果你看这里:https://en.wikipedia.org/wiki/Double-precision_floating-point_format 你会发现,这个格式是由:
pow 返回的数字需要更高的精度 (63) 才能准确存储。
基本上,您添加的1 低于此精度阈值。
相比之下,long 的精度为 64 位。
为了更清楚,假设我们使用十进制而不是base2:
在精度为2 的一些虚构的小浮点数据类型中,值1000 将存储为1.00e3。如果添加 1,则必须将其存储为 1.001e3。但由于我们只有 2 的精度,它只能存储 1.00e3 并且没有任何变化。所以1.00e3 + 1 == 1.00e3
在您的示例中也发生了同样的情况,只是我们正在处理更大的数字和原因基数。
【讨论】:
你应该使用括号来合并结果,然后减去 1,如下所示:
System.out.println((long)Math.pow(2,63));
System.out.println(((long)(Math.pow(2,63))-1));
输出:
9223372036854775807
9223372036854775806
【讨论】:
对于java中的long数据类型,最大值为9,223,372,036,854,775,807(含)。 (2^63 -1)
所以即使你尝试
System.out.println((long)Math.pow(2,65));
System.out.println((long)(Math.pow(2,63)-1));
输出
9223372036854775807
9223372036854775807
【讨论】: