为什么 Java 认为从10到99的所有数字的乘积都是0?

下面的代码块给出的输出为0。

public class HelloWorld{


public static void main(String []args){
int product = 1;
for (int i = 10; i <= 99; i++) {
product *= i;
}
System.out.println(product);
}
}

有人能解释一下为什么会这样吗?

31381 次浏览

看起来像是 整数溢出

看看这个

BigDecimal product=new BigDecimal(1);
for(int i=10;i<99;i++){
product=product.multiply(new BigDecimal(i));
}
System.out.println(product);

产出:

25977982938941930515945176761070443325092850981258133993315252362474391176210383043658995147728530422794328291965962468114563072000000000000000000000

输出不再是 int值。那么由于溢出,您将得到错误的值。

如果溢出,则返回最小值,并从 在那里。如果它下流,它回到最大值和 从那里继续。

更多 信息

编辑。

让我们按如下方式更改代码

int product = 1;
for (int i = 10; i < 99; i++) {
product *= i;
System.out.println(product);
}

输出:

10
110
1320
17160
240240
3603600
57657600
980179200
463356416
213837312
-18221056
-382642176
171806720
-343412736
348028928
110788608
-1414463488
464191488
112459776
-1033633792
-944242688
793247744
-385875968
150994944
838860800
-704643072
402653184
2013265920
-805306368
-1342177280
-2147483648
-2147483648>>>binary representation is 11111111111111111111111111101011 10000000000000000000000000000000
0 >>> here binary representation will become 11111111111111111111111111101011 00000000000000000000000000000000
----
0

在中间的某个位置,你得到了 0作为产品,所以,你的整个产品将是0。

就你而言:

for (int i = 10; i < 99; i++) {
if (product < Integer.MAX_VALUE)
System.out.println(product);
product *= i;
}
// System.out.println(product);


System.out.println(-2147483648 * EvenValueOfi); // --> this is the culprit (Credits : Kocko's answer )


O/P :
1
10
110
1320
17160
240240
3603600
57657600
980179200
463356416
213837312
-18221056
-382642176
171806720
-343412736
348028928
110788608
-1414463488
464191488
112459776
-1033633792
-944242688
793247744
-385875968
150994944
838860800
-704643072
402653184
2013265920
-805306368
-1342177280  --> Multiplying this and the current value of `i` will also give -2147483648 (INT overflow)
-2147483648  --> Multiplying this and the current value of `i` will also give -2147483648 (INT overflow)


-2147483648  ->  Multiplying this and the current value of 'i' will give 0 (INT overflow)
0
0
0

每次将 i的当前值与得到的 0作为输出的数字相乘。

如果我运行这个代码,我得到的所有

          1 * 10 =          10
10 * 11 =         110
110 * 12 =        1320
1320 * 13 =       17160
17160 * 14 =      240240
240240 * 15 =     3603600
3603600 * 16 =    57657600
57657600 * 17 =   980179200
980179200 * 18 =   463356416 <- Integer Overflow (17643225600)
463356416 * 19 =   213837312
213837312 * 20 =   -18221056
-18221056 * 21 =  -382642176
-382642176 * 22 =   171806720
171806720 * 23 =  -343412736
-343412736 * 24 =   348028928
348028928 * 25 =   110788608
110788608 * 26 = -1414463488
-1414463488 * 27 =   464191488
464191488 * 28 =   112459776
112459776 * 29 = -1033633792
-1033633792 * 30 =  -944242688
-944242688 * 31 =   793247744
793247744 * 32 =  -385875968
-385875968 * 33 =   150994944
150994944 * 34 =   838860800
838860800 * 35 =  -704643072
-704643072 * 36 =   402653184
402653184 * 37 =  2013265920
2013265920 * 38 =  -805306368
-805306368 * 39 = -1342177280
-1342177280 * 40 = -2147483648
-2147483648 * 41 = -2147483648
-2147483648 * 42 =           0 <- produce 0
0 * 43 =           0

整数溢出原因-

980179200 * 18 =   463356416 (should be 17643225600)


17643225600 : 10000011011100111100100001000000000 <-Actual
MAX_Integer :     1111111111111111111111111111111
463356416   :     0011011100111100100001000000000 <- 32 bit Integer

产生0原因-

-2147483648 * 42 =           0 (should be -90194313216)


-90194313216: 1010100000000000000000000000000000000 <- Actual
MAX_Integer :       1111111111111111111111111111111
0           :      00000000000000000000000000000000 <- 32 bit Integer

下面是该程序在每个步骤中的作用:

          1 * 10 =          10
10 * 11 =         110
110 * 12 =        1320
1320 * 13 =       17160
17160 * 14 =      240240
240240 * 15 =     3603600
3603600 * 16 =    57657600
57657600 * 17 =   980179200
980179200 * 18 =   463356416
463356416 * 19 =   213837312
213837312 * 20 =   -18221056
-18221056 * 21 =  -382642176
-382642176 * 22 =   171806720
171806720 * 23 =  -343412736
-343412736 * 24 =   348028928
348028928 * 25 =   110788608
110788608 * 26 = -1414463488
-1414463488 * 27 =   464191488
464191488 * 28 =   112459776
112459776 * 29 = -1033633792
-1033633792 * 30 =  -944242688
-944242688 * 31 =   793247744
793247744 * 32 =  -385875968
-385875968 * 33 =   150994944
150994944 * 34 =   838860800
838860800 * 35 =  -704643072
-704643072 * 36 =   402653184
402653184 * 37 =  2013265920
2013265920 * 38 =  -805306368
-805306368 * 39 = -1342177280
-1342177280 * 40 = -2147483648
-2147483648 * 41 = -2147483648
-2147483648 * 42 =           0
0 * 43 =           0
0 * 44 =           0
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
0 * 97 =           0
0 * 98 =           0

注意,在某些步骤中,乘法的结果是一个较小的数字(980179200 * 18 = 463356416)或不正确的符号(213837312 * 20 = -18221056) ,表明存在整数溢出。但是0是从哪里来的呢?继续读。

请记住 int数据类型 是一个32位签名的两个人的互补整数,下面是每个步骤的说明:

Operation         Result(1)     Binary Representation(2)                                           Result(3)
----------------  ------------  -----------------------------------------------------------------  ------------
1 * 10            10                                                               1010            10
10 * 11           110                                                            1101110           110
110 * 12          1320                                                        10100101000          1320
1320 * 13         17160                                                    100001100001000         17160
17160 * 14        240240                                                 111010101001110000        240240
240240 * 15       3603600                                             1101101111110010010000       3603600
3603600 * 16      57657600                                         11011011111100100100000000      57657600
57657600 * 17     980179200                                     111010011011000101100100000000     980179200
980179200 * 18   17643225600                               100 00011011100111100100001000000000     463356416
463356416 * 19    8803771904                                10 00001100101111101110011000000000     213837312
213837312 * 20    4276746240                                   11111110111010011111100000000000     -18221056
-18221056 * 21    -382642176  11111111111111111111111111111111 11101001001100010101100000000000    -382642176
-382642176 * 22   -8418127872  11111111111111111111111111111110 00001010001111011001000000000000     171806720
171806720 * 23    3951554560                                   11101011100001111111000000000000    -343412736
-343412736 * 24   -8241905664  11111111111111111111111111111110 00010100101111101000000000000000     348028928
348028928 * 25    8700723200                                10 00000110100110101000000000000000     110788608
110788608 * 26    2880503808                                   10101011101100010000000000000000   -1414463488
-1414463488 * 27  -38190514176  11111111111111111111111111110111 00011011101010110000000000000000     464191488
464191488 * 28   12997361664                                11 00000110101101000000000000000000     112459776
112459776 * 29    3261333504                                   11000010011001000000000000000000   -1033633792
-1033633792 * 30  -31009013760  11111111111111111111111111111000 11000111101110000000000000000000    -944242688
-944242688 * 31  -29271523328  11111111111111111111111111111001 00101111010010000000000000000000     793247744
793247744 * 32   25383927808                               101 11101001000000000000000000000000    -385875968
-385875968 * 33  -12733906944  11111111111111111111111111111101 00001001000000000000000000000000     150994944
150994944 * 34    5133828096                                 1 00110010000000000000000000000000     838860800
838860800 * 35   29360128000                               110 11010110000000000000000000000000    -704643072
-704643072 * 36  -25367150592  11111111111111111111111111111010 00011000000000000000000000000000     402653184
402653184 * 37   14898167808                                11 01111000000000000000000000000000    2013265920
2013265920 * 38   76504104960                             10001 11010000000000000000000000000000    -805306368
-805306368 * 39  -31406948352  11111111111111111111111111111000 10110000000000000000000000000000   -1342177280
-1342177280 * 40  -53687091200  11111111111111111111111111110011 10000000000000000000000000000000   -2147483648
-2147483648 * 41  -88046829568  11111111111111111111111111101011 10000000000000000000000000000000   -2147483648
-2147483648 * 42  -90194313216  11111111111111111111111111101011 00000000000000000000000000000000             0
0 * 43             0                                                                  0             0
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
0 * 98             0                                                                  0             0
  1. 正确的结果
  2. 是结果的内部表示(64位用于说明)
  3. 是由下32位的两个互补所表示的结果

我们知道把一个数乘以一个偶数:

  • 将位向左移动,并向右添加零位
  • 结果是偶数

所以基本上你的程序会重复地将一个偶数乘以另一个数这样就会从右边开始把结果位归零。

PS: 如果乘法只涉及奇数,那么结果不会变成零。

由于许多现有的答案都指向 Java 的实现细节和调试输出,让我们来看看二进制乘法背后的数学原理,从而真正解释其中的原因。

@ kaspard 的评论方向是正确的。假设你不直接用这个数字乘,而是用这个数字的素因子乘。大多数数字都是2作为质因数。在二进制中,这等于左移。通过交换性,我们可以先乘以2的素因子。也就是说我们只要左转就行了。

查看二进制乘法规则时,只有当两个操作数值都是1时,1才会导致特定的数字位置。

因此,左移的效果是,当进一步乘以结果时,1的最低位位置增加。

由于整数只包含最低阶位,所以当结果中包含质因子2的次数足够多时,它们都将被设置为0。

注意,对于这个分析,2的补数表示并不感兴趣,因为乘法结果的符号可以独立于结果数字来计算。这意味着,如果该值溢出并变为负值,则最低阶位表示为1,但在乘法过程中,它们再次被视为0。

最终,计算溢出,并且最终溢出导致产品为零; 这发生在 product == -2147483648i == 42时。试试这段代码,自己验证一下(或者运行代码 给你) :

import java.math.BigInteger;


class Ideone {
public static void main (String[] args) throws java.lang.Exception {
System.out.println("Result: " + (-2147483648 * 42));
}
}

一旦它为零,它当然会保持为零。下面是一些能够产生更精确结果的代码(您可以运行代码 给你) :

import java.math.BigInteger;


class Ideone {
public static void main (String[] args) throws java.lang.Exception {
BigInteger p = BigInteger.valueOf(1);
BigInteger start = BigInteger.valueOf(10);
BigInteger end = BigInteger.valueOf(99);
for(BigInteger i = start; i.compareTo(end) < 0; i = i.add(BigInteger.ONE)){
p = p.multiply(i);
System.out.println("p: " + p);
}
System.out.println("\nProduct: " + p);
}
}

这是因为整数溢出。当你把许多偶数相乘时,二进制数会得到很多后面的零。当你有超过32个后跟零的 int,它滚动到 0

为了帮助您可视化这一点,下面是对不会溢出的数字类型计算的十六进制乘法。看看后面的零是如何慢慢增长的,注意 int是由最后8个十六进制数组成的。在乘以42(0x2A)之后,int的所有32位都是零!

                                     1 (int: 00000001) * 0A =
A (int: 0000000A) * 0B =
6E (int: 0000006E) * 0C =
528 (int: 00000528) * 0D =
4308 (int: 00004308) * 0E =
3AA70 (int: 0003AA70) * 0F =
36FC90 (int: 0036FC90) * 10 =
36FC900 (int: 036FC900) * 11 =
3A6C5900 (int: 3A6C5900) * 12 =
41B9E4200 (int: 1B9E4200) * 13 =
4E0CBEE600 (int: 0CBEE600) * 14 =
618FEE9F800 (int: FEE9F800) * 15 =
800CE9315800 (int: E9315800) * 16 =
B011C0A3D9000 (int: 0A3D9000) * 17 =
FD1984EB87F000 (int: EB87F000) * 18 =
17BA647614BE8000 (int: 14BE8000) * 19 =
25133CF88069A8000 (int: 069A8000) * 1A =
3C3F4313D0ABB10000 (int: ABB10000) * 1B =
65AAC1317021BAB0000 (int: 1BAB0000) * 1C =
B1EAD216843B06B40000 (int: 06B40000) * 1D =
142799CC8CFAAFC2640000 (int: C2640000) * 1E =
25CA405F8856098C7B80000 (int: C7B80000) * 1F =
4937DCB91826B2802F480000 (int: 2F480000) * 20 =
926FB972304D65005E9000000 (int: E9000000) * 21 =
12E066E7B839FA050C309000000 (int: 09000000) * 22 =
281CDAAC677B334AB9E732000000 (int: 32000000) * 23 =
57BF1E59225D803376A9BD6000000 (int: D6000000) * 24 =
C56E04488D526073CAFDEA18000000 (int: 18000000) * 25 =
1C88E69E7C6CE7F0BC56B2D578000000 (int: 78000000) * 26 =
43C523B86782A6DBBF4DE8BAFD0000000 (int: D0000000) * 27 =
A53087117C4E76B7A24DE747C8B0000000 (int: B0000000) * 28 =
19CF951ABB6C428CB15C2C23375B80000000 (int: 80000000) * 29 =
4223EE1480456A88867C311A3DDA780000000 (int: 80000000) * 2A =
AD9E50F5D0B637A6610600E4E25D7B00000000 (int: 00000000)

计算机乘法实际上发生在模2 ^ 32上。一旦在被乘数中积累了足够的2的幂,那么所有的值都将为0。

这里我们有这个序列中所有的偶数,以及除以这个数的最大二次幂,和二的累积幂

num   max2  total
10    2     1
12    4     3
14    2     4
16    16    8
18    2     9
20    4    11
22    2    12
24    8    15
26    2    16
28    4    18
30    2    19
32    32   24
34    2    25
36    4    27
38    2    28
40    8    31
42    2    32

直到42的乘积等于 x * 2 ^ 32 = 0(mod 2 ^ 32)。两个幂的顺序与格雷码(以及其他事物)有关,表现为 https://oeis.org/A001511

编辑: 要了解为什么对这个问题的其他回答是不完整的,请考虑这样一个事实,即同一个程序,仅限于奇数整数,将 没有收敛到0,尽管所有的溢出。

它是一个整数溢出。

Int 数据类型为4字节,即32位。因此,大于2 ^ (32-1)-1(2,147,483,647)的数字不能存储在此数据类型中。您的数值将不正确。

对于非常大的数字,您需要导入并使用类 java.math.BigInteger:

BigInteger product = BigInteger.ONE;
for (long i = 10; i < 99; i++)
product = product.multiply(BigInteger.valueOf(i));
System.out.println(product.toString());

注意: 如果数值对于 int 数据类型来说仍然太大,但是小到足以容纳8个字节(绝对值小于或等于2 ^ (64-1)-1) ,那么您可能应该使用 long原语。

HackerRank 的实践问题( www.HackerRank.com ) ,例如算法实践部分(https://www.hackerrank.com/domains/algorithms/warmup)包括一些非常好的大量问题,这些问题提供了关于如何思考使用合适的数据类型的良好实践。