我正在尝试编写一个代码来确定自1970年初以来的毫秒数何时将超过long的容量。以下代码似乎可以完成这项工作:
public class Y2K {
public static void main(String[] args) {
int year = 1970;
long cumSeconds = 0;
while (cumSeconds < Long.MAX_VALUE) {
// 31557600000 is the number of milliseconds in a year
cumSeconds += 3.15576E+10;
year++;
}
System.out.println(year);
}
}
此代码在几秒钟内执行并打印292272992.如果不使用科学记数法,我将cumSeconds写为31558000000L
,程序似乎“永远”运行(我只是在10分钟左右后暂停)。另请注意,以科学计数形式编写cumSeconds不需要指定数字为long
,最后为L或l。
答案 0 :(得分:41)
它产生影响的原因是因为科学记数3.1558E+10
是double
字面值,而字面31558000000L
当然是long
字面值。
这在the +=
operator中有所不同。
E1 op = E2形式的复合赋值表达式等效于E1 =(T)((E1)op(E2)),其中T是E1的类型,但E1仅被评估一次。
基本上,long + = long产生一个long,但long + = double也产生一个long。
添加double
时,cumSeconds
的初始值会扩展为double
,然后会发生添加。结果会narrowing primitive conversion返回long
。
将浮点数转换为整数类型T需要两个步骤:
- 在第一步中,如果T为长
,则将浮点数转换为long 醇>
(剪断)
否则,以下两种情况之一必须为真:
值必须太小(大幅度或负无穷大的负值),第一步的结果是int或long类型的最小可表示值。
值必须太大(大幅度或正无穷大的正值),第一步的结果是最大可表示的类型int或long 。< / p>
(大胆强调我的)
结果最终太大而无法在long
中表示,因此结果缩小为Long.MAX_VALUE
,while
循环结束。
但是,当您使用long
字面值时,您会不断向偶数值添加偶数值,最终会溢出。这不会将值设置为Long.MAX_VALUE
,这是奇数,因此循环是无限的。
但是,使用Java 1.8+而不依赖于最终产生Long.MAX_VALUE
的添加,您可以使用Math.addExact
显式测试溢出。
返回其参数的总和,如果结果溢出long,则抛出异常。
<强>抛出:强>
ArithmeticException
- 如果结果溢出了很长的
答案 1 :(得分:6)
关键的观察结果是cumSeconds < Long.MAX_VALUE
其中cumSeconds
是long
只有cumSeconds
正好Long.MAX_VALUE
才能为假。
如果使用长数进行计算,则需要相当长的时间才能准确地达到此值(如果达到此值),因为当您离开数字范围时,长算术会回绕。
当double值足够大时,使用双数进行算术将产生最大值。
答案 2 :(得分:5)
@rgettman已经详细介绍了当您使用double
代替long
时发生的四舍五国体操。但还有更多。
当您向long
重复添加一个大号时,您最终会得到一个否定结果。例如,Long.MAX_VALUE + 1L = Long.MIN_VALUE
。当发生这种情况时,您将无限期地重复该过程。
因此,如果您将代码更改为:
while (cumSeconds >= 0L) {
// 31557600000 is the number of milliseconds in a year
cumSeconds += 31557600000L;
你会发现事情变得消极,因为cumSeconds
已经过去了。