У меня простая программа:
public class Mathz {
static int i = 1;
public static void main(String[] args) {
while (true){
i = i + i;
System.out.println(i);
}
}
}
Когда я запускаю эту программу, все , что я вижу 0
на i
в моем выходе. Я ожидал, что в первый раз у нас будет i = 1 + 1
, а i = 2 + 2
затем и i = 4 + 4
т. Д.
Это связано с тем, что, как только мы пытаемся повторно объявить i
слева, его значение сбрасывается до 0
?
Если бы кто-нибудь мог указать мне более тонкие детали этого, это было бы здорово.
Измените значение int
на, long
и кажется, что числа печатаются должным образом. Я удивлен, насколько быстро он достигает максимального 32-битного значения!
источник
0
на первых нескольких итерациях, но скорость вывода скрывает этот факт от OP). Почему это принято?Введение
Проблема в целочисленном переполнении. Если он переполняется, он возвращается к минимальному значению и продолжается оттуда. Если он становится недостаточным, он возвращается к максимальному значению и продолжается оттуда. На изображении ниже показан одометр. Я использую это для объяснения переполнения. Это механический переполнение, но все же хороший пример.
В одометре,
max digit = 9
превышение максимального означает9 + 1
, что переносится и дает0
; Однако нет более высокой цифры, которую можно было бы изменить на a1
, поэтому счетчик сбрасывается наzero
. Вы уловили идею - сейчас на ум приходят «целочисленные переполнения».Таким образом,
2147483647 + 1
переполняется и оборачивается-2147483648
. Следовательноint i=2147483647 + 1
, будет переполнено, что не равно2147483648
. Кроме того, вы говорите «он всегда печатает 0». Это не так, потому что http://ideone.com/WHrQIW . Ниже эти 8 чисел показывают точку, в которой он вращается и переполняется. Затем он начинает печатать нули. Кроме того, не удивляйтесь, насколько быстро он вычисляет, современные машины быстры.Почему целочисленное переполнение "оборачивается"
Исходный PDF
источник
Нет, он не печатает только нули.
Измените его на это, и вы увидите, что произойдет.
То, что происходит, называется переполнением.
источник
true
наi<10000
:)while(k --> 0)
просторечии называемый «покаk
идет к0
»;)вывод:
источник
Поскольку у меня недостаточно репутации, я не могу опубликовать изображение вывода для той же программы на C с контролируемым выводом, вы можете попробовать себя и убедиться, что он действительно печатает 32 раза, а затем, как объяснялось, из-за переполнения i = 1073741824 + 1073741824 изменяется на -2147483648 и еще одно добавление выходит за пределы диапазона int и превращается в Zero.
источник
system("deltree C:")
вы находитесь в DOS / Windows). Знаковое целочисленное переполнение - это неопределенное поведение в C / C ++, в отличие от Java. Будьте очень осторожны при использовании такой конструкции.signed and unsigned
целых чисел без какого-либо неопределенного поведенияi += i
для 32+ итераций, а затем имелif (i > 0)
. Компилятор может оптимизировать это,if(true)
так как, если мы всегда добавляем положительные числа,i
всегда будет больше 0. Он также может оставить условие, где оно не будет выполняться, из-за переполнения, представленного здесь. Поскольку компилятор может создать из этого кода две равнозначные программы, его поведение не определено.Значение
i
хранится в памяти с использованием фиксированного количества двоичных цифр. Когда для числа требуется больше цифр, чем доступно, сохраняются только самые низкие цифры (самые высокие цифры теряются).Сложение
i
с собой - это то же самое, что умножениеi
на два. Точно так же, как умножение числа на десять в десятичной системе счисления можно выполнить, сдвигая каждую цифру влево и ставя ноль справа, умножение числа на два в двоичной системе счисления может выполняться таким же образом. Это добавляет одну цифру справа, поэтому цифра теряется слева.Здесь начальное значение равно 1, поэтому, если мы используем 8 цифр для хранения
i
(например),00000001
00000010
00000100
и так до последнего ненулевого шага
10000000
00000000
Независимо от того, сколько двоичных цифр выделено для хранения числа и независимо от начального значения, в конечном итоге все цифры будут потеряны, поскольку они смещены влево. После этого продолжение удвоения числа не изменит число - оно все равно будет представлено всеми нулями.
источник
Это правильно, но после 31 итерации 1073741824 + 1073741824 вычисляет некорректно и после этого выводит только 0.
Вы можете выполнить рефакторинг для использования BigInteger, чтобы ваш бесконечный цикл работал правильно.
источник
int
.long
может представлять большие числа, чемint
может.Для отладки таких случаев полезно уменьшить количество итераций в цикле. Используйте это вместо своего
while(true)
:Затем вы можете видеть, что он начинается с 2 и удваивает значение, пока не вызовет переполнение.
источник
Я буду использовать 8-битное число для иллюстрации, потому что его можно полностью подробно описать в коротком месте. Шестнадцатеричные числа начинаются с 0x, а двоичные - с 0b.
Максимальное значение для 8-битного целого числа без знака - 255 (0xFF или 0b11111111). Если вы добавите 1, вы обычно ожидаете получить: 256 (0x100 или 0b100000000). Но поскольку это слишком много бит (9), это превышает максимум, поэтому первая часть просто отбрасывается, оставляя вам фактически 0 (0x (1) 00 или 0b (1) 00000000, но с отброшенным 1).
Итак, когда ваша программа запускается, вы получаете:
источник
Самый большой десятичный литерал типа
int
- 2147483648 (= 2 31 ). Все десятичные литералы от 0 до 2147483647 могут появляться везде, где может появиться литерал int, но литерал 2147483648 может появляться только как операнд унарного оператора отрицания -.Если целочисленное сложение переполняется, результатом являются младшие биты математической суммы, представленные в некотором достаточно большом формате с дополнением до двух. Если происходит переполнение, то знак результата не совпадает со знаком математической суммы двух значений операндов.
источник