为什么用科学记数法写一个数字会在这个代码中有所作为?

2022-09-01 20:13:58

我正在尝试编写一个代码来确定自1970年初以来的毫秒数何时会超过一个长的能力。以下代码似乎可以完成这项工作:

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写成,则该程序似乎需要“永远”才能运行(我只是在10分钟左右后暂停)。另请注意,用科学记数法编写 cumSeconds 不需要指定数字是 a,末尾有 L 或 l。31558000000Llong


答案 1

它之所以有所作为,是因为科学记数法数字是文字,而文字当然是文字。3.1558E+10double31558000000Llong

这使得 += 运算符的所有差异。

形式为 E1 op= E2 的复合赋值表达式等效于 E1 = (T) ((E1) op (E2)),其中 T 是 E1 的类型,只是 E1 只计算一次。

基本上,多头+=多头产生多头,但多头+=双倍也产生多头。

当添加 a 时,的初始值被加宽为 a,然后发生加法。结果将进行缩小的基元转换回 。doublecumSecondsdoublelong

将浮点数缩小到整数类型 T 的转换需要两个步骤:

  1. 在第一步中,如果 T 是长整型,则将浮点数转换为长整型

(截图)

  • 否则,必须满足以下两种情况之一:

    • 该值必须太小(大幅度或负无穷大的负值),并且第一步的结果是int或long类型的最小可表示值。

    • 该值必须太大(大幅度或正无穷大的正值),并且第一步的结果是int或long类型的最大可表示值

(粗体强调我的)

结果最终太大而无法用 a 表示,因此结果被缩小到 ,循环结束。longLong.MAX_VALUEwhile

但是,当您使用文本时,您将不断向偶数值添加偶数值,这最终将溢出。这不会将值设置为 ,这是奇数,因此循环是无限的。longLong.MAX_VALUE

但是,与其依赖于加法最终产生,不如使用Java 1.8 +,您可以使用Math.addExact显式测试溢出。Long.MAX_VALUE

返回其参数的总和,如果结果溢出长整型,则引发异常。

抛出:

ArithmeticException- 如果结果溢出很长


答案 2

关键的观察结果是,where is a 只能是假的,如果 恰好是 。cumSeconds < Long.MAX_VALUEcumSecondslongcumSecondsLong.MAX_VALUE

如果您使用长数字进行计算,则需要相当长的时间才能完全达到此值(如果达到该值),因为当您离开数字范围时,长算术会绕行。

当双精度值足够大时,使用双精度数字进行算术运算将产生最大值。


推荐