为什么这两个乘法运算有不同的结果?

为什么我需要添加一个“L”字母来获得正确的长值? 另一个价值是什么?

long oneYearWithL = 1000*60*60*24*365L; long oneYearWithoutL = 1000*60*60*24*365; System.out.println(oneYearWithL);//gives correct calculation result : 31536000000 System.out.println(oneYearWithoutL)//gives incorrect calculation result: 1471228928 
 long oneYearWithL = 1000*60*60*24*365L; long oneYearWithoutL = 1000*60*60*24*365; 

你的第一个值实际上是一个long(因为365L是一个long ,而1000*60*60*24是一个integer ,所以multiplying一个longmultiplying一个integer数值的结果是一个long值。

但第二个值是一个整数(因为你只是integer数值的integer数值,所以结果是一个32-bit整数,现在得到的结果是在实际的整数范围之外,所以在得到分配给variables,它将被截断以适应有效的整数范围。

看看下面的打印声明:

 System.out.println(1000*60*60*24*365L); System.out.println(1000*60*60*24*365); System.out.println(Integer.MAX_VALUE); 

当你运行上面的代码时: –

输出: –

 31536000000 1471228928 2147483647 

所以,你可以看到不同之处

 011101010111101100010010110000000000 -- Binary equivalent of 1000*60*60*24*365L 01111111111111111111111111111111 -- Binary equivalent of Integer.MAX_VALUE 

所以,如果你不在数字的末尾添加L ,则从第一个二进制string中删除4个最重要的位。

所以,string变成..

 (0111)01010111101100010010110000000000 -- Remove the most significant bits.. 01010111101100010010110000000000 -- Binary equivalent of 1471228928 

(你得到的输出)


更新: –从上面的解释,你也可以理解,即使在第一次赋值,如果你multiplication integers乘以365L的结果超出范围,然后再次将被截断,以适应整数范围,或者如果需要,转换为2's complement representation ,然后只有它将乘以long value - 365L

例如: –

 long thirtyYearWithL = 1000*60*60*24*30*365L; 

在上面的例子中,考虑第一部分 – 1000*60*60*24*30 。 这个乘法的结果是: – 2592000000 。 现在让我们看看它是如何表示在binary equivalent : –

 2592000000 = 10011010011111101100100000000000 -- MSB is `1`, a negative value 01100101100000010011100000000001 -- 2's complement representation 

2's complement表示的十进制表示是1702967297 。 所以, 2592000000转换为-1702967297 ,在乘以365L之前。 现在,这个值适合integer range : – [-2147483648 to 2147483647] ,所以它不会被截断。

所以,实际结果将是: –

 long thirtyYearWithL = 1000*60*60*24*30*365L; = 2592000000 * 365L; = -1702967297 * 365L = -621583063040 

所以,所有这些东西只考虑应用算术运算的最终结果的实际type 。 并且这个检查是在left to right的操作的每个临时结果上执行的(考虑到操作符具有left-to-right关联性)。 如果发现任何暂时的结果超出范围,则在进行下一个操作之前,将其相应地转换为适合的范围。


更新2: –

所以,而不是: –

 long thirtyYearWithL = 1000*60*60*24*30*365L; 

如果你在开始时移动你的365L ,那么你会得到正确的结果: –

 long thirtyYearWithL = 365L*1000*60*60*24*30; // will give you correct result 

因为,现在你的temporary结果将是long型的,并且能够保持这个价值。

没有L你的计算是作为一个32位值进行的。 如果将值表示为hex,则较小的值只是较大值的较低4个字节。

Java默认为32位整数types。 长, L ,是64位。 通过在365之后放置L ,你告诉编译器将365视为一个long值。 当32位和64位值相乘时,编译器将32位值上传到64位,以便评估expression式的中间结果保留完整的64位范围。

请参阅Java语言规范中的原语types和值

乘法的行为在https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.1中明确定义,如下所示:;

15.17.1乘法运算符*

二进制*运算符执行乘法,产生其操作数的乘积。 如果操作数expression式没有副作用,乘法是交换操作。 当操作数全部是相同types时,整数乘法是关联的,而浮点乘法不是关联的。 如果整数乘法溢出,那么结果就是math乘积的低位 ,用一些足够大的二进制补码格式表示。 因此,如果发生溢出,则结果的符号可能与两个操作数值的math乘积的符号不同。

浮点乘法的结果受IEEE 754algorithm规则的控制:

  • 如果任一操作数是NaN ,则结果是NaN
  • 如果结果不是NaN ,那么如果两个操作数具有相同的符号,则结果的符号为正;如果操作数具有不同的符号,则结果的符号为负。
  • 无限乘以零会导致NaN。
  • 无穷乘以有限值导致有符号无穷。 标志由上述规则决定。
  • 在余下的情况下,既不涉及无穷大也不涉及NaN,则计算确切的math积。 然后select一个浮点值集合:
    • 如果乘法expression式是FP-strict(第15.4节):
      • 如果乘法expression式的types是float ,那么必须selectfloat值集合。
      • 如果乘法expression式的types是double ,那么必须selectdouble值集合。
    • 如果乘法expression式不是FP-strict:
      • 如果乘法expression式的types是float ,那么可以select浮点值集合或浮点扩展指数值集合。
      • 如果乘法expression式的types是double ,那么可以selectdouble值集合或double-extended-exponent值集合。

接下来,必须从select的值集合中select一个值来表示产品。

如果产品的规模太大,我们说操作溢出; 结果是一个无限的适当的标志。

否则,使用IEEE 754轮到最近模式将产品四舍五入到所选值的最接近值。 Java编程语言要求支持IEEE 754(§4.2.4)定义的逐渐下溢。

尽pipe可能发生溢出,下溢或丢失信息的事实,但乘法运算符*求值从不会引发运行时exception。