Следующий блок кодов дает на выходе 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);
}
}
Кто-нибудь может объяснить, почему это происходит?
java
integer
integer-overflow
Анируддха Саркар
источник
источник
2
появляться примерно 90 раз. Это означает, что вам понадобится переменная минимум с 90 битами, чтобы получить ненулевой вывод. 32 и 64 оба меньше 90. Чтобы вычислить целые числа больше, чем родные слова, вы должны использовать любой большой целочисленный класс, доступный на выбранном вами языке.Ответы:
Вот что программа делает на каждом этапе:
Обратите внимание, что на некоторых этапах умножение приводит к меньшему числу (980179200 * 18 = 463356416) или неправильному знаку (213837312 * 20 = -18221056), что указывает на переполнение целого числа. Но откуда берется ноль? Читай дальше.
Имея в виду , что
int
тип данных является 32-разрядное знаковое , двоичное дополнение целое, вот объяснение каждого шага:Мы знаем, что умножение числа на четное число:
Таким образом, в основном ваша программа многократно умножает четное число на другое число, которое обнуляет результирующие биты, начиная справа
PS: Если умножения включали нечетные числа только тогда, результат не станет нулевым.
источник
Компьютерное умножение действительно происходит по модулю 2 ^ 32. Как только вы накопите достаточное количество степеней двойки в множимом, все значения будут равны 0.
Здесь мы имеем все четные числа в ряду, а также максимальную степень двух, которая делит число, и совокупную степень двух
Произведение до 42 равно x * 2 ^ 32 = 0 (mod 2 ^ 32). Последовательность степеней двух связана с кодами Грея (среди прочего) и выглядит как https://oeis.org/A001511 .
РЕДАКТИРОВАТЬ: чтобы понять, почему другие ответы на этот вопрос являются неполными, примите во внимание тот факт, что та же программа, ограниченная только нечетными целыми числами, не сходится к 0, несмотря на все переполнение.
источник
Это выглядит как целочисленное переполнение .
Взгляните на это
Вывод:
Выход больше не является
int
значением. Тогда вы получите неправильное значение из-за переполнения.Больше информации
Редактировать .
Давайте изменим ваш код следующим образом
Вывод:
источник
Это из-за целочисленного переполнения. Когда вы умножаете много четных чисел вместе, двоичное число получает много конечных нулей. Когда у вас более 32 конечных нулей для an
int
, он переключается на0
.Чтобы помочь вам визуализировать это, вот умножение в шестнадцатеричном формате, рассчитанное на тип числа, которое не будет переполнено. Посмотрите, как медленно растут конечные нули, и обратите внимание, что an
int
состоит из последних 8 шестнадцатеричных цифр. После умножения на 42 (0x2A) все 32 битаint
равны нулю!источник
Где-то посередине вы получаете
0
как продукт. Итак, весь ваш продукт будет равен 0.В твоем случае :
Каждый раз, когда вы умножаете текущее значение
i
на число, полученное0
на выходе.источник
Поскольку многие из существующих ответов указывают на детали реализации Java и вывода отладки, давайте посмотрим на математику двоичного умножения, чтобы действительно ответить, почему.
Комментарий @kasperd идет в правильном направлении. Предположим, вы умножаете не непосредственно на число, а на простые множители этого числа. Чем много чисел будет иметь 2 в качестве простого множителя. В двоичном формате это равно сдвигу влево. По коммутативности мы можем сначала умножить на простые множители 2. Это означает, что мы просто делаем сдвиг влево.
Если взглянуть на двоичные правила умножения, единственный случай, когда 1 приведет к определенной позиции цифры, - это когда оба значения операнда равны единице.
Таким образом, эффект сдвига влево заключается в том, что позиция самого младшего бита 1 при дальнейшем умножении результата увеличивается.
Поскольку целое число содержит только биты самого младшего порядка, все они будут установлены в 0, когда в результате достаточно часто используется простой множитель 2.
Обратите внимание, что представление дополнения до двух не представляет интереса для этого анализа, так как знак результата умножения может быть вычислен независимо от полученного числа. Это означает, что если значение переполняется и становится отрицательным, биты младшего разряда представляются как 1, но во время умножения они снова обрабатываются как 0.
источник
Если я запущу этот код, что я получу все -
Причина целочисленного переполнения -
Произвести 0 причина -
источник
В конце концов, вычисление переполняется, и в конечном итоге это переполнение приводит к нулю; такое бывает, когда
product == -2147483648
иi == 42
. Попробуйте этот код, чтобы убедиться в этом сами (или запустите код здесь ):Как только это ноль, это конечно остается нулем. Вот код, который даст более точный результат (вы можете запустить код здесь ):
источник
Это целочисленное переполнение.
Тип данных int составляет 4 байта или 32 бита. Следовательно, числа больше 2 ^ (32 - 1) - 1 (2 147 483 647) не могут быть сохранены в этом типе данных. Ваши числовые значения будут неверными.
Для очень больших чисел вы захотите импортировать и использовать класс
java.math.BigInteger:
ПРИМЕЧАНИЕ. Для числовых значений, которые все еще слишком велики для типа данных int, но достаточно малы, чтобы уместиться в пределах 8 байтов (абсолютное значение меньше или равно 2 ^ (64 - 1) - 1), вам, вероятно, следует использовать
long
примитив.Практические проблемы HackerRank (www.hackerrank.com), такие как практический раздел Алгоритмы ( https://www.hackerrank.com/domains/algorithms/warmup ), включают в себя несколько очень хороших вопросов большого количества, которые дают хорошую практику о том, как подумайте о соответствующем типе данных для использования.
источник