为什么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); } }
请有人可以解释为什么会发生这种情况?
这是程序在每一步所做的事情:
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),表示存在整数溢出。 但零从哪里来? 阅读。
请记住, int
数据types是一个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
- 是正确的结果
- 是结果的内部表示(64位用于说明)
- 是由低32位的二进制补码表示的结果
我们知道把一个数字乘以一个偶数:
- 向左移位并向右加零位
- 导致偶数
所以基本上你的程序将一个偶数乘以另一个数字,从右边开始将结果位清零。
PS:如果乘法涉及奇数,那么结果不会变成零。
计算机乘法实际上是模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,尽pipe所有的溢出。
它看起来像一个整数溢出 。
看看这个
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
这是因为整数溢出。 当你将许多偶数相乘时,二进制数会得到大量的尾随零。 当一个int
有超过32个尾随零时,它将回滚到0
。
为了帮助你可视化这个,下面是在一个不会溢出的数字types上计算的hex乘法。 查看拖尾零点是如何缓慢增长的,并注意int
是由最后8个hex数字组成的。 乘以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)
在中间的某个地方,你得到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
每当你乘以当前值的数字你得到0
作为输出。
由于现有的很多答案指向Java的实现细节和debugging输出,让我们来看看二进制乘法背后的math来真正回答原因。
@ kasperd的评论朝着正确的方向发展。 假设你不直接乘以数字,而是乘以该数字的主要因素。 比很多数字都有2个作为主要因素。 在二进制中,这相当于一个左移。 通过交换性,我们可以首先乘以2的素数。 这意味着我们只是左转。
查看二进制乘法规则时,唯一的情况是1将导致特定的数字位置,即两个操作数值都为1时。
所以左移的效果是当进一步乘以结果时1的最低位位置增加。
由于整数只包含最低位,所以当素数因子2在结果中足够频繁时,它们都将被设置为0。
请注意,由于乘法结果的符号可以从结果数中独立计算,因此这种分析的补码表示forms对此分析没有意义。 这意味着如果数值溢出并且变为负数,则最低阶位表示为1,但在乘法期间,它们再次被视为0。
最终,计算溢出,最终溢出导致零的产品; product == -2147483648
和i == 42
。 试一试这个代码来validation它(或者在这里运行代码):
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); } }
如果我运行这个代码我得到的所有 –
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
这是一个整数溢出。
int数据types是4个字节或32位。 因此,大于2 ^(32 – 1) – 1(2,147,483,647)的数字不能被存储在这个数据types中。 你的数值将是不正确的。
对于非常大的数字,您将需要导入并使用类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数据types仍然太大的数值,但是足够小到8字节(绝对值小于或等于2 ^(64 – 1) – 1)的数值,应该使用long
原语。
HackerRank的实践问题(www.hackerrank.com),如algorithm练习部分,( https://www.hackerrank.com/domains/algorithms/warmup )包含了一些非常好的大量问题,给出了如何考虑使用适当的数据types。