IntStream.reduce()返回错误结果



我的代码:

public class Main {
public static void main(String[] args) {
System.out.println(arraySign(new int[]{41,65,14,80,20,10,55,58,24,56,28,86,96,10,3,
84,4,41,13,32,42,43,83,78,82,70,15,-41}));
}
public static int arraySign(int[] nums) {
int product = Arrays.stream(nums).reduce(1, (acc, a) -> acc * a);
if (product != 0)
return product / Math.abs(product);
return product;
}
}

预期结果:-1.

你能解释一下为什么上面的方法返回0吗?

首先,正如在评论中提到的那样,您会得到整数溢出。对于乘法,它发生得非常快。

我们可以使用BigInteger来可视化这些值:

BigInteger productBi = Arrays.stream(nums)
.mapToObj(BigInteger::valueOf)
.reduce(BigInteger.ONE, (acc, a) -> {
System.out.println(acc);
return acc.multiply(a);
});
对于您的样例数据,代码将产生:
1
41
2665
37310
2984800
59696000
596960000
32832800000 // <- already larger than Integer.MAX_VALUE
...

它表明溢出发生在计算的一开始。为了找到乘积变为零的点,让我们切换到初始代码:

int product = Arrays.stream(nums).reduce(1, (acc, a) -> {
System.out.println(acc + " * " + a);
return acc * a;
});

会产生:

1 * 41
41 * 65
2665 * 14
...
1342177280 * 32 // <- here product turs to zero
0 * 42
...
more zero

显然,134217728032相乘得到0。让我们使用Integer.toBinaryString():

查看这些二进制数字
Bits: 1010000000000000000000000000000 // 1342177280
Bits: 100000                          // 32

根据§15.17.1。Java语言规范,当发生溢出时,只保留低阶位。在这种情况下,所有的低阶位都是零,因此乘积将不包含有效位,结果将是0

如果整数乘法溢出,则结果为低阶位用足够大的双补格式表示的数学乘积。

最新更新