为什么这两个乘法运算给出不同的结果?15.17.1 乘法运算符 *

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

答案 1
long oneYearWithL = 1000*60*60*24*365L;
long oneYearWithoutL = 1000*60*60*24*365;

您的第一个值实际上是一个长整型(因为 是 一个 ,并且是一个 ,因此具有值的值的结果是一个值。365Llong1000*60*60*24integermultiplyinglongintegerlong

但第二个值是一个整数(因为您只用一个值来多重计算一个值。因此,结果将是一个整数。现在,获得的结果超出了整数的实际范围。因此,在分配给变量之前,会将其截断以适合有效的整数范围。integerinteger32-bitmultiplication

请看以下打印声明: -

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

(您作为输出获得)


更新: -从上面的解释中,你也可以理解,即使在第一个赋值中,如果在你的结果之前把它乘以超出范围,那么它将再次被截断以适合整数范围,或者如果需要,则转换为,然后只有它将与.multiplicationintegers365L2's complement representationlong value - 365L

例如: -

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

在上面的示例中,请考虑第一部分 - 。此乘法的结果是: - .现在让我们看看它是如何表示的: -1000*60*60*24*302592000000binary equivalent

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

表示的十进制表示形式为 。因此,在乘以之前转换为 。现在,由于此值适合于 is : - ,因此它不会被进一步截断。2's complement17029672972592000000-1702967297365Linteger range[-2147483648 to 2147483647]

因此,实际结果将是: -

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

因此,所有这些东西都只是考虑了应用算术运算时最终结果的实际结果。并且此检查对操作移动的每个临时结果执行(考虑具有关联性的运算符)。如果发现任何临时结果超出范围,则在继续下一个操作之前,将相应地将其转换为适合所需的范围。typeleft to rightleft-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

因为,现在您的结果将是 类型 ,并且能够保持该值。temporarylong


答案 2

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

Java 默认为 32 位整数类型。对于 Long,是 64 位。通过在 之后放置 ,您可以告诉编译器将 365 视为值。当 32 位和 64 位值相乘时,编译器会将 32 位值上调为 64 位,以便计算表达式的中间结果保留完整的 64 位范围。LL365long

请参见 Java 语言规范中的基元类型和值

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

15.17.1 乘法运算符 *

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

浮点乘法的结果受 IEEE 754 算术规则的约束:

  • 如果任一操作数为 ,则结果为 。NaNNaN
  • 如果结果不是 ,则如果两个操作数具有相同的符号,则结果的符号为正;如果操作数具有不同的符号,则结果的符号为负。NaN
  • 将无穷大乘以零得到 NaN。
  • 将无穷大乘以有限值得到有符号无穷大。该标志由上述规则确定。
  • 在其余情况下,既不涉及无穷大也不涉及NaN,则计算确切的数学乘积。然后选择浮点值集:
    • 如果乘法表达式是 FP 严格表达式 (§15.4):
      • 如果乘法表达式的类型是 ,则必须选择浮点值集。float
      • 如果乘法表达式的类型是 ,则必须选择双精度值集。double
    • 如果乘法表达式不是 FP 严格表达式:
      • 如果乘法表达式的类型是 ,则可以在实现的心血来潮中选择浮点值集或浮点扩展指数值集。float
      • 如果乘法表达式的类型是 ,则可以在实现的心血来潮中选择双精度值集或双扩展指数值集。double

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

如果产物的幅度太大而无法表示,我们说操作溢出;结果就是一个无穷大的适当符号。

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

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


推荐