Почему 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.
AShelly

45
Какой? Почему этот вопрос был закрыт как дубликат вопроса, который закрывается как дубликат этого вопроса ?
Салман A

82
Получил 99 проблем и 2147483648 aint 1.
Glenatron

Ответы:


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
Шестнадцатеричное представление помогло мне разобраться в том, что здесь происходило. Спасибо за разъяснение!

1
Да, было бы более поучительно, если бы вы изменили свою программу, чтобы также распечатывать шестнадцатеричные значения в длинном списке.
Hot Licks

4
Этот ответ правильный, но в нем так много беспорядка. Последние пять строк - это сердце, и нигде не видно, как именно это вступает в игру. (Но можно разобраться в этом с гигантского стола.)
Рекс Керр

6
Иными словами, вы накапливаете множители 2. Некоторые числа дают вам несколько множителей 2, например, 12, 16 и 20. Каждый множитель 2 будет сдвигать вправо все биты всех ваших последующих результатов, оставляя нули как заполнители. После 32-кратного сдвига вправо у вас не останется ничего, кроме 32 нулей-заполнителей.
Сильно

2
Аналогичный эффект также можно увидеть в базе 10. Попробуйте умножить любую серию последовательных целых чисел, каждый раз, когда вы умножаете на число, кратное 10, вы добавляете хотя бы один ноль в конец произведения, и удалить этот ноль невозможно от произведения путем умножения целых чисел. В какой-то момент все n-е младшие значащие цифры будут заполнены нулями, и если вы выполняете арифметику по модулю 10 ** m (что приводит к отсечению всего, кроме 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 .

РЕДАКТИРОВАТЬ: чтобы понять, почему другие ответы на этот вопрос являются неполными, примите во внимание тот факт, что та же программа, ограниченная только нечетными целыми числами, не сходится к 0, несмотря на все переполнение.


Ура! Наконец-то правильный ответ. Люди должны больше замечать этот ответ!
Рекс Керр

Это единственный правильный ответ. Все остальные не объясняют почему .
Оливье Грегуар

5
@ OlivierGrégoire Я не согласен; Я думаю, что принятый ответ является правильным и дает совершенно хорошее объяснение. Этот просто более прямой.
Дэвид Z

1
Я надеюсь, что больше людей увидят этот ответ. Основная причина указана здесь!
Lanpa

1
@DavidZ: согласовано; Принятый ответ в основном правильный - мой пост не содержит «почему» моего вступительного предложения. Но окончание принятого ответа ближе всего к ответу на «почему ноль», но оно не объясняет «почему 42» - есть только 16 четных чисел от 10 до 42
user295691

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 конечных нулей для an int, он переключается на 0.

Чтобы помочь вам визуализировать это, вот умножение в шестнадцатеричном формате, рассчитанное на тип числа, которое не будет переполнено. Посмотрите, как медленно растут конечные нули, и обратите внимание, что an 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-битным значением на каждом этапе, подчеркивая, что значение усекается ...
kwah

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 раз, результат равен нулю.
Руах

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)
user295691

Да, но переполнение не приводит к нулю и не дает результата до 42-й итерации.
Тревор

Я думаю, что я имею в виду, что вы не отвечаете на вопрос «почему» - почему совокупный продукт вообще может проходить через 0? Ответ Тима С. дает некоторое представление о том, почему, но реальный ответ заключается в модульной арифметике.
user295691

Вопрос не задает, почему продукт проходит через ноль. Он спрашивает, почему код выдает ноль. Другими словами, я думаю, что OP больше интересуется динамикой языка Java, чем модульной арифметикой, но, возможно, я ошибаюсь. Это был бы не первый раз, когда я неверно истолковал чей-то вопрос.
Тревор

например, если бы эта программа взяла произведение всех нечетных чисел от 11 до 99, то оно не достигло бы нуля. Ваш ответ на самом деле не касается, почему это произойдет.
user295691

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.