Чому Java вважає, що добуток усіх чисел від 10 до 99 дорівнює 0?


131

Наступний блок кодів дає вихід 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);
    }
}

Скажіть, будь ласка, хтось пояснить, чому це відбувається?


106
Ви, ймовірно, маєте ціле переповнення.
TheLostMind

68
Якщо ви враховуєте основні фактори в продукті, ви з'явитеся 2приблизно в 90 разів. Це означає, що вам знадобиться змінна принаймні 90 біт, щоб отримати ненульовий вихід. 32 і 64 обидва менше 90. Для обчислення цілих чисел, більших за рідні слова, ви повинні використовувати будь-який великий цілочисельний клас у вибраній мові.
kasperd

62
Технічно це добуток чисел від 10 до 98.
Ашеллі

45
Що? Чому це питання було закрито як дублікат запитання, яке закривається як дублікат цього питання ?
Салман А

82
Отримав 99 проблем і 2147483648 не 1.
Гленатрон

Відповіді:


425

Ось що програма робить на кожному кроці:

          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тип даних є 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: Якщо у множеннях задіяні лише непарні числа, то результат не стане нульовим.


15
Hex Представництво - це те, що допомогло мені взяти голову навколо того, що тут відбувалося. Дякуємо за уточнення!

1
Так, було б більш повчально, якби ви змінили програму, щоб також надрукувати шістнадцяткові значення у довгому списку.
Гарячі лизання

4
Ця відповідь правильна, але так багато захаращеності. Останні п’ять рядків є сутністю цього, і ніде він насправді не ілюструє, де саме це входить у гру. (Але можна зібрати його з гігантської таблиці.)
Рекс Керр

6
Інакше кажучи, ви накопичуєте коефіцієнти 2. Деякі числа дають вам множинні коефіцієнти 2 самі по собі, як 12, 16 і 20. Кожен фактор 2 буде зміщувати всі біти всіх наступних результатів, залишаючи нулі як заповнювачі. Після того як ви 32 рази перемістили праворуч, у вас залишиться нічого, крім 32 нулів заповнення.
Кін

2
Аналогічний ефект можна побачити і в базі 10. Спробуйте помножити будь-який ряд послідовних цілих чисел, кожного разу, коли ви множите число, ділене на 10, ви додаєте принаймні один нуль до кінця добутку, і неможливо видалити цей нуль від добутку множенням цілих чисел. У якийсь момент усі n-ті найменші значущі цифри будуть заповнені нулями, і якщо ви виконуєте арифметику з модулем 10 ** м (що має наслідком відсікання всього, окрім m-го найменш значущих цифр), тоді воно перетвориться на нуль. Так само і з будь-якими іншими основами.
Лі Лі Райан

70

Комп'ютерне множення дійсно відбувається за модулем 2 ^ 32. Після того, як ви накопичили достатню потужність двох у мультиплікації, то всі значення будуть дорівнювати 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 .

EDIT: щоб дізнатись, чому інші відповіді на це питання неповні, врахуйте той факт, що та сама програма, обмежена лише непарними цілими числами, не збігатиметься до 0, незважаючи на всю переповнення.


Так! Нарешті, правильна відповідь. Люди повинні більше помітити цю відповідь!
Рекс Керр

Це єдина правильна відповідь. Всі інші не пояснюють, чому .
Олів'є Грегоар

5
@ OlivierGrégoire Я не згоден; Я вважаю, що прийнята відповідь є правильною і дає абсолютно гарне пояснення. Цей просто більш прямий.
David Z

1
Я сподіваюся, що більше людей побачать цю відповідь. Тут вказано першопричину!
lanpa

1
@DavidZ: Погоджено; прийнята відповідь здебільшого правильна - мій пост не дуже стосується "чому" мого вступного речення. Але близька прийнята відповідь є найбільш близькою до відповіді "чому нуль", але вона не пояснює "чому 42" - є лише 16 парних чисел між 10 і 42
користувач295691

34

Це схоже на ціле переповнення .

Погляньте на це

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

22

Це через ціле переповнення. Коли ви множите багато парних чисел разом, двійкове число отримує безліч зворотних нулів. Якщо у вас більше 32 кінцевих нулів для антителі int, воно перекидається на 0.

Щоб допомогти вам візуалізувати це, ось множення у шістнадцятковій формі, обчислене на тип числа, який не переповнюватиметься. Подивіться, як повільні нулі повільно зростають, і зауважте, що а intскладається з останніх 8 шістнадцяткових цифр. Помноживши на 42 (0x2A), всі 32 біти а - intце нулі!

                                     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)

1
Це трохи вводить в оману. Хоча це правильно демонструє, чому воно переходить до нуля, кожне значення зберігається в 32-бітовому int після множення, тому його слід усікати після кожного кроку. Те, як ви написали свою відповідь, означає, що вона не обрізається, поки цикл for не припиняється. Було б краще, якби ви показали лише останні 8 цифр для кожного кроку.
RyNo

@KamikazeScotsman Я вдосконалив свою відповідь на основі вашої пропозиції. Менше зайвих нулів, більше 32-розрядна видимість int.
Тім С.

1
+1 для показу фактичного значення проти 32-бітного значення на кожному етапі, підкреслюючи, що це значення
врізається

14

Десь посередині ви отримуєте 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як вихід.


@KickButtowski - Помножте числа .. Ви будете знати, чому: P
TheLostMind

@KickButtowski - 0, помножений на будь-яке інше число, постійно призведе до 0 назавжди після того, як переповнення поверне 0 у будь-якій точці.
Містер Муз

Я, але я думаю, ви повинні бути більш інформативними, щоб і інші могли навчитися
Kick Buttowski

@KickButtowski - оновив відповідь. Перевірте частину OP.
TheLostMind

8
@KickButtowski: Це тому, що перекриття переповнення відбувається двома силами. По суті, ОП обчислює {10 x 11 x 12 x ... x 98} модуль 2 ^ 32. Оскільки множини 2 з’являються набагато більше 32 разів у цьому продукті, результат дорівнює нулю.
ruakh

12

Оскільки багато існуючих відповідей вказують на деталі впровадження Java та вихід налагодження, давайте подивимось на математику, що стоїть за двійковим множенням, щоб дійсно відповісти на те, чому.

Коментар @kasperd йде в правильному напрямку. Припустимо, ви не множите безпосередньо на число, а на прості множники цього числа. Тоді багато чисел матимуть 2 як основний фактор. У двійковому це дорівнює зсуву вліво. За комутативністю ми можемо помножити на прості коефіцієнти 2 перших. Це означає, що ми просто робимо ліву зміну.

Переглядаючи правила двійкового множення, єдиний випадок, коли 1 призведе до певної позиції цифри, коли обидва значення операнда є одиницями.

Таким чином, ефект лівого зсуву полягає в тому, що нижча бітова позиція 1 при подальшому множенні результату збільшується.

Оскільки ціле число містить лише біти найнижчого порядку, всі вони будуть встановлені на 0, коли основний коефіцієнт 2 виставляється досить часто в результаті.

Зауважимо, що представлення комплементу двох не представляє інтерес для цього аналізу, оскільки знак результату множення можна обчислити незалежно від отриманого числа. Це означає, що якщо значення переповнюється і стає негативним, біти нижнього порядку представляються як 1, але під час множення вони знову трактуються як 0.


7

Якщо я запускаю цей код Що я отримую -

          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

6

Врешті-решт, обчислення переповнюється, і з часом це переповнення призводить до добутку нуля; що відбувається, коли product == -2147483648і i == 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);
    }
}

Він переповнює (у точному значенні цього слова) задовго до 42-ї ітерації - о 19-му вже перекинуто, оскільки f (19) <f (18)
користувач295691

Так, але переповнення не призводить до продукту нуля до 42-ї ітерації та не призводить до цього.
Тревор

Я здогадуюсь, що я звертаюсь до того, що ви не звертаєтесь до "чому" - чому б кумулятивний продукт коли-небудь проходив через 0? Відповідь Тіма С. дає певну вказівку на те, чому, але реальна відповідь полягає в модульній арифметиці.
користувач295691

Питання не запитує, чому продукт проходить через нуль. Він запитує, чому код виробляє нуль. Іншими словами, я думаю, що ОП більше зацікавлена ​​в динаміці мови Java, ніж в модульній арифметиці, але, можливо, я помиляюся. Це був би не перший раз, коли я неправильно трактував чиєсь питання.
Тревор

наприклад, якби ця програма взяла добуток усіх непарних чисел від 11 до 99, то вона не досягне нуля. Ваша відповідь насправді не стосується того, чому це станеться.
користувач295691

1

Це ціле переповнення.

Тип даних 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 ), включають кілька дуже хороших запитань із великою кількістю, які дають гарну практику щодо того, як подумайте про відповідний тип даних, який слід використовувати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.