为什么用科学记数法编写数字会对此代码产生影响?

sed*_*deh 24 java scientific-notation long-integer

我正在尝试编写一个代码来确定自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);
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码在几秒钟内执行并打印292272992.如果不是使用科学记数法,我将cumSeconds写为31558000000L,程序似乎"永远"运行(我只是在10分钟左右后暂停).另请注意,以科学计数形式写入cumSeconds不需要指定数字最后为longL或l.

rge*_*man 41

它产生影响的原因是因为科学记数字3.1558E+10double文字,而文字31558000000L当然是long文字.

这使得所有的差异+=操作.

形式E1 op = E2的复合赋值表达式等效于E1 =(T)((E1)op(E2)),其中T是E1的类型,除了E1仅被评估一次.

基本上,long + = long产生一个long,但long + = double也产生一个long.

添加a时double,将初始值cumSeconds加宽为a double,然后进行加法.结果经历了缩小的原始转换long.

将浮点数转换为整数类型T需要两个步骤:

  1. 在第一步中,如果T很长,则浮点数转换为long

(剪断)

  • 否则,以下两种情况之一必须为真:

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

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

(大胆强调我的)

结果最终太大而无法在a中表示long,因此结果缩小到Long.MAX_VALUE,并且while循环结束.

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

但是Long.MAX_VALUE,使用Java 1.8+,您可以明确地测试溢出,而不是依赖于最终产生的加法Math.addExact.

返回其参数的总和,如果结果溢出long则抛出异常.

抛出:

ArithmeticException - 如果结果溢出很长时间

  • 注意:这也意味着`x + = 0.0`或甚至`x + = 0.0f`会导致长时间丢失精度. (8认同)

Hen*_*nry 6

关键的发现是,cumSeconds < Long.MAX_VALUE这里cumSeconds是一个long,如果只能是假的cumSeconds也正是Long.MAX_VALUE.

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

当double值足够大时,使用双数进行算术将产生最大值.


Eri*_*rom 5

@rgettman已经详细介绍了当你使用double而不是a long.时发生的围绕体操.但还有更多.

当你反复向a添加一个大数字时long,你最终会得到一个负面结果.例如,Long.MAX_VALUE + 1L = Long.MIN_VALUE.当发生这种情况时,您将无限期地重复该过程.

因此,如果您将代码更改为:

    while (cumSeconds >= 0L) {
        // 31557600000 is the number of milliseconds in a year
        cumSeconds += 31557600000L;
Run Code Online (Sandbox Code Playgroud)

你会发现事情变得消极,因为cumSeconds滚了过来.