为什么这两个乘法运算有不同的结果?
为什么我需要添加一个“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
一个long
值multiplying
一个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。