乘法发生溢出
Overflow occurs with multiplication
long m = 24 * 60 * 60 * 1000 * 1000;
以上代码产生了溢出并且没有打印出正确的结果。
long m2 = 24L * 60 * 60 * 1000 * 1000;
long m3 = 24 * 60 * 60 * 1000 * 1000L;
以上两行打印出正确的结果。
我的问题是-
m2
或 m3
对我使用的编译器有影响吗?
- java 是如何开始繁殖的?从左到右还是从右到左?是先计算 24*60 还是先计算 1000*1000?
在这种情况下 -
long m = 24 * 60 * 60 * 1000 * 1000;
先评估赋值权。右边没有 long
类型的数据。都是int
。所以 JVM
尝试将结果放入 int
然后发生溢出。
在第二种情况下 -
long m2 = 24L * 60 * 60 * 1000 * 1000;
long m3 = 24 * 60 * 60 * 1000 * 1000L;
这里乘法的一个操作数是long
。所以其他人会自动提示long
。结果试图适应 long
。最后用 m2
和 m3
完成赋值。
是的,乘法的结合性从左到右 - 意味着先取左操作数。基于这个事实,我认为在这种情况下我们应该使用 -
long m2 = 24L * 60 * 60 * 1000 * 1000;
这个语句,因为在这个语句中升级到 long
发生得更早,这降低了溢出的风险。
我会使用 m2
行而不是 m3
行。
Java 从 left to right 计算乘法运算符 *
,因此首先计算 24 * 60
。
碰巧24 * 60 * 60 * 1000
(一个1000
)没有溢出,所以当你乘以1000L
(第二个1000
) , 乘积在相乘前被提升为 long
,所以不会发生溢出。
但是正如您在评论中提到的,在乘以最后一个 long
数字之前,更多因素可能导致 int
数据类型溢出,从而产生不正确的答案。最好对第一个(最左边的)数字使用 long
文字,如 m2
中那样,以避免从一开始就溢出。或者,您可以将第一个文字转换为 long
,例如(long) 24 * 60 * ...
.
从左到右相乘,int * int
产生 int
。所以
24 * 60 * 60 * 1000 * 1000
与
相同
(((24 * 60)* 60) * 1000) * 1000
这给了我们
(((1440)* 60) * 1000) * 1000
(( 86400 ) * 1000) * 1000
( 86400000 ) * 1000
最后由于整数溢出(因为 86400000000
对于最大值为 2147483647
的整数来说太大)结果将是
500654080
您可以通过使用 long
作为参数之一来消除整数溢出(int * long
和 long * int
产生 long
)。
在这种情况下,您可以像在 m2
的情况下那样在开始时执行此操作:24L * 60
将产生 long
1440L
再次乘以int 60
产生新的 long
,依此类推,只产生 long
值。
m3
案例在这里有效,因为您将 86400000
乘以 1000L
,这意味着您正在避免整数溢出,因为结果将是 long
.
Since expressions are evaluated from left to right,我更喜欢你的第一个解决方案 (m2 = ...
)。
推理:让我们看一个稍微不同的例子。
long g = Integer.MAX_VALUE * 2 * 2L;
此表达式的计算结果为 -4
,因为只有最后一次乘法将第一个表达式转换为 long
(此时为 -2
,因为两个操作数都是 int
).如果你写
long g = Integer.MAX_VALUE * 2L * 2;
相反,g
将保留 8589934588
的预期值,因为第一个乘法产生 long
.
类型的结果
让我们乘以更多的数字,即使有 1000L
:
这一行也会溢出
long m3 = 24 * 60 * 60 * 1000 * 1000 * 1000 * 1000L;
虽然这会给出正确的结果:
long m3 = 24L * 60 * 60 * 1000 * 1000 * 1000 * 1000;
所以我们确定 java 从左到右开始相乘,我们必须从左边开始 Long
以防止溢出。
这是因为当我们使用 long 作为一个操作数时,另一个所有 int
类型的操作数都会被提示为 long
。
java 中的表达式从左到右求值。
long m = 24 * 60 * 60 * 1000 * 1000;
以上代码产生了溢出并且没有打印出正确的结果。
long m2 = 24L * 60 * 60 * 1000 * 1000;
long m3 = 24 * 60 * 60 * 1000 * 1000L;
以上两行打印出正确的结果。
我的问题是-
m2
或m3
对我使用的编译器有影响吗?- java 是如何开始繁殖的?从左到右还是从右到左?是先计算 24*60 还是先计算 1000*1000?
在这种情况下 -
long m = 24 * 60 * 60 * 1000 * 1000;
先评估赋值权。右边没有 long
类型的数据。都是int
。所以 JVM
尝试将结果放入 int
然后发生溢出。
在第二种情况下 -
long m2 = 24L * 60 * 60 * 1000 * 1000;
long m3 = 24 * 60 * 60 * 1000 * 1000L;
这里乘法的一个操作数是long
。所以其他人会自动提示long
。结果试图适应 long
。最后用 m2
和 m3
完成赋值。
是的,乘法的结合性从左到右 - 意味着先取左操作数。基于这个事实,我认为在这种情况下我们应该使用 -
long m2 = 24L * 60 * 60 * 1000 * 1000;
这个语句,因为在这个语句中升级到 long
发生得更早,这降低了溢出的风险。
我会使用 m2
行而不是 m3
行。
Java 从 left to right 计算乘法运算符 *
,因此首先计算 24 * 60
。
碰巧24 * 60 * 60 * 1000
(一个1000
)没有溢出,所以当你乘以1000L
(第二个1000
) , 乘积在相乘前被提升为 long
,所以不会发生溢出。
但是正如您在评论中提到的,在乘以最后一个 long
数字之前,更多因素可能导致 int
数据类型溢出,从而产生不正确的答案。最好对第一个(最左边的)数字使用 long
文字,如 m2
中那样,以避免从一开始就溢出。或者,您可以将第一个文字转换为 long
,例如(long) 24 * 60 * ...
.
从左到右相乘,int * int
产生 int
。所以
24 * 60 * 60 * 1000 * 1000
与
相同(((24 * 60)* 60) * 1000) * 1000
这给了我们
(((1440)* 60) * 1000) * 1000
(( 86400 ) * 1000) * 1000
( 86400000 ) * 1000
最后由于整数溢出(因为 86400000000
对于最大值为 2147483647
的整数来说太大)结果将是
500654080
您可以通过使用 long
作为参数之一来消除整数溢出(int * long
和 long * int
产生 long
)。
在这种情况下,您可以像在 m2
的情况下那样在开始时执行此操作:24L * 60
将产生 long
1440L
再次乘以int 60
产生新的 long
,依此类推,只产生 long
值。
m3
案例在这里有效,因为您将 86400000
乘以 1000L
,这意味着您正在避免整数溢出,因为结果将是 long
.
Since expressions are evaluated from left to right,我更喜欢你的第一个解决方案 (m2 = ...
)。
推理:让我们看一个稍微不同的例子。
long g = Integer.MAX_VALUE * 2 * 2L;
此表达式的计算结果为 -4
,因为只有最后一次乘法将第一个表达式转换为 long
(此时为 -2
,因为两个操作数都是 int
).如果你写
long g = Integer.MAX_VALUE * 2L * 2;
相反,g
将保留 8589934588
的预期值,因为第一个乘法产生 long
.
让我们乘以更多的数字,即使有 1000L
:
long m3 = 24 * 60 * 60 * 1000 * 1000 * 1000 * 1000L;
虽然这会给出正确的结果:
long m3 = 24L * 60 * 60 * 1000 * 1000 * 1000 * 1000;
所以我们确定 java 从左到右开始相乘,我们必须从左边开始 Long
以防止溢出。
这是因为当我们使用 long 作为一个操作数时,另一个所有 int
类型的操作数都会被提示为 long
。
java 中的表达式从左到右求值。