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

时间:2012-10-06 09:05:53

标签: java int long-integer multiplication operation

为什么我需要添加“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

2 个答案:

答案 0 :(得分:132)

long oneYearWithL = 1000*60*60*24*365L;
long oneYearWithoutL = 1000*60*60*24*365;

您的第一个值实际上很长(由于365Llong,而1000*60*60*24integer,因此multiplying a {的结果带有long值的{1}}值为integer值。

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

请看下面的打印声明: -

multiplication

运行上述代码时: -

输出: -

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 ,那么从第一个二进制字符串中删除4个最高位...

因此,字符串变为..

L

(作为输出获得)


更新: - 从上面的解释中,您还可以理解,即使在第一个作业中,如果(0111)01010111101100010010110000000000 -- Remove the most significant bits.. 01010111101100010010110000000000 -- Binary equivalent of 1471228928 multiplication的{​​{1}}与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,即{ - 1}},因此不会进一步截断。

因此,实际结果将是: -

[-2147483648 to 2147483647]

所以,所有这些东西只考虑应用算术运算的最终结果的实际long thirtyYearWithL = 1000*60*60*24*30*365L; = 2592000000 * 365L; = -1702967297 * 365L = -621583063040 。并且对从type移动的每个临时操作结果执行此检查(考虑具有left to right关联性的运算符)。如果发现任何临时结果超出范围,则在进行下一步操作之前,相应地转换为适合所需范围。


更新2: -

所以,而不是: -

left-to-right

如果您在开始时移动long thirtyYearWithL = 1000*60*60*24*30*365L; ,那么您将获得正确的结果: -

365L

因为,现在您的long thirtyYearWithL = 365L*1000*60*60*24*30; // will give you correct result 结果属于temporary类型,并且能够保留该值。

答案 1 :(得分:15)

如果没有L,您的计算将以32位值执行。如果将值表示为十六进制,则较小的值只是较大值的较低4个字节。

Java默认为32位整数类型。对于Long,L是64位。通过在L之后放置365,您告诉编译器将365视为long值。当32位和64位值相乘时,编译器将32位值向上转换为64位,以便评估表达式的中间结果保留整个64位范围。

请参阅Java Language Specification

中的原始类型和值

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

  

15.17.1乘法运算符*

     

二进制*运算符执行乘法运算,生成其操作数的乘积。如果操作数表达式没有副作用,则乘法是可交换操作。当操作数都是相同类型时,整数乘法是关联的,而浮点乘法不是关联的。 如果整数乘法溢出,则结果是数学乘积的低阶位,如某些足够大的二进制补码格式所示。因此,如果发生溢出,则结果的符号可能与两个操作数值的数学乘积的符号不同。

     

浮点乘法的结果由IEEE 754算法的规则决定:

     
      
  • 如果任一操作数为NaN,则结果为NaN
  •   
  • 如果结果不是NaN,如果两个操作数具有相同的符号,则结果的符号为正,如果操作数具有不同的符号,则结果为负。
  •   
  • 将无穷大乘以零会导致NaN。
  •   
  • 无穷大乘以有限值会产生有符号无穷大。该标志由上述规则决定。
  •   
  • 在其余情况下,无论是无穷大还是NaN,都会计算出精确的数学乘积。然后选择一个浮点值集:      
        
    • 如果乘法表达式是FP-strict(第15.4节):      
          
      • 如果乘法表达式的类型为float,则必须选择浮点值集。
      •   
      • 如果乘法表达式的类型为double,则必须选择双值集。
      •   
    •   
    • 如果乘法表达式不是FP严格的:      
          
      • 如果乘法表达式的类型是float,则可以根据实现的方式选择浮点值集或浮点扩展指数值集。
      •   
      • 如果乘法表达式的类型是double,那么可以选择双值集或双扩展指数值集,这是实现的一时兴起。
      •   
    •   
  •   
     

接下来,必须从所选的值集中选择一个值来表示产品。

     

如果产品的大小太大而无法表示,我们说操作溢出;结果就是无穷无尽的适当标志。

     

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

     

尽管可能发生溢出,下溢或信息丢失,但乘法运算符*的评估从不会引发运行时异常。