Почему я = я + я дает мне 0?
У меня есть простая программа:
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
в левой части, его значение получает от reset до 0
?
Если кто-то может указать мне на более тонкие детали этого, это будет здорово.
Измените int
на long
и, кажется, печатайте цифры, как ожидалось. Я удивлен тем, как быстро он достигает максимального 32-битного значения!
Ответы
Ответ 1
Проблема связана с целочисленным переполнением.
В 32-битной арифметике с двумя дополнениями:
i
действительно начинаю с значений степени двойки, но затем начинается переполнение, когда вы получаете 2 30:
2 30 + 2 30= -2 31
-2 31 + -2 31= 0
... в int
арифметике, так как это по существу арифметический мод 2 ^ 32.
Ответ 2
Введение
Проблема состоит в переполнении целых чисел. Если он переполняется, он возвращается к минимальному значению и продолжается оттуда. Если он переполняется, он возвращается к максимальному значению и продолжается оттуда. Ниже изображен одометр. Я использую это для объяснения переполнения. Это механическое переполнение, но хороший пример.
В одометре max digit = 9
, поэтому выходит за пределы максимального значения 9 + 1
, которое переносится и дает a 0
; Однако нет более высокой цифры для изменения , поэтому счетчик сбрасывается до zero
. Вы получаете идею - теперь на ум приходит "целое переполнение".
![]()
![enter image description here]()
![enter image description here]()
Самый большой десятичный литерал типа int равен 2147483647 (2 31 -1). Все десятичные литералы от 0 до 2147483647 могут появляться в любом месте int буква может появиться, но буква 2147483648 может отображаться только как операнд унарного оператора отрицания -.
Если целочисленное добавление переполняется, то результатом является низкий порядок биты математической суммы, представленные в некоторых достаточно больших двухкомпонентный формат. Если происходит переполнение, то знак результат не совпадает с признаком математической суммы двух значения операндов.
Таким образом, 2147483647 + 1
переполняется и обтекает до -2147483648
. Следовательно, int i=2147483647 + 1
будет переполнено, что не равно 2147483648
. Кроме того, вы говорите: "он всегда печатает 0". Это не так, потому что http://ideone.com/WHrQIW. Ниже, эти 8 чисел показывают точку, в которой он поворачивается и переполняется. Затем он начинает печатать 0s. Кроме того, не удивляйтесь, как быстро это вычисляется, машины сегодняшнего дня бывают быстрыми.
268435456
536870912
1073741824
-2147483648
0
0
0
0
Почему целочисленное переполнение "обтекает"
Оригинальный PDF
Ответ 3
Нет, он не печатает только нули.
Измените это, и вы увидите, что произойдет.
int k = 50;
while (true){
i = i + i;
System.out.println(i);
k--;
if (k<0) break;
}
Что происходит, называется переполнение.
Ответ 4
static int i = 1;
public static void main(String[] args) throws InterruptedException {
while (true){
i = i + i;
System.out.println(i);
Thread.sleep(100);
}
}
out put:
2
4
8
16
32
64
...
1073741824
-2147483648
0
0
when sum > Integer.MAX_INT then assign i = 0;
Ответ 5
Поскольку у меня недостаточно репутации, я не могу опубликовать изображение вывода для той же самой программы на C с контролируемым выходом, и вы можете попробовать себя и увидеть, что она на самом деле печатает 32 раза, а затем, как объясняется из-за переполнения я = 1073741824 + 1073741824 изменяется на
-2147483648 и еще одно добавление выходит за пределы диапазона int и переходит в Zero.
#include<stdio.h>
#include<conio.h>
int main()
{
static int i = 1;
while (true){
i = i + i;
printf("\n%d",i);
_getch();
}
return 0;
}
Ответ 6
Значение i
сохраняется в памяти с использованием фиксированного количества двоичных цифр. Когда номеру требуется больше цифр, чем доступно, сохраняются только самые низкие цифры (самые высокие цифры теряются).
Добавление i
к себе такое же, как умножение i
на два. Точно так же, как умножение числа на десять в десятичной нотации может выполняться путем сдвига каждой цифры влево и ввода нуля справа, умножение числа на два в двоичной нотации может выполняться одинаково. Это добавляет одну цифру справа, поэтому цифра теряется слева.
Здесь начальное значение равно 1, поэтому, если мы используем 8 цифр для хранения i
(например),
- после 0 итераций значение
00000001
- после 1-й итерации значение
00000010
- после 2 итераций значение
00000100
и т.д. до окончательного ненулевого шага
- после 7 итераций значение
10000000
- после 8 итераций значение
00000000
Независимо от того, сколько двоичных цифр выделено для хранения номера, и независимо от начального значения, в конце концов все цифры будут потеряны, поскольку они будут сдвинуты влево. После этого точка, продолжая удваивать число, не изменит номер - он все равно будет представлен всеми нулями.
Ответ 7
Это правильно, но после 31 итерации 1073741824 + 1073741824 не вычисляется правильно и после этого печатает только 0.
Вы можете реорганизовать использование BigInteger, поэтому ваш бесконечный цикл будет работать правильно.
public class Mathz {
static BigInteger i = new BigInteger("1");
public static void main(String[] args) {
while (true){
i = i.add(i);
System.out.println(i);
}
}
}
Ответ 8
Для отладки таких случаев полезно уменьшить количество итераций в цикле. Используйте это вместо while(true)
:
for(int r = 0; r<100; r++)
Затем вы можете увидеть, что он начинается с 2 и удваивает значение до тех пор, пока оно не вызывает переполнение.
Ответ 9
Я буду использовать 8-битное число для иллюстрации, потому что он может быть полностью детализирован в коротком пространстве. Число шестнадцатеричных чисел начинается с 0x, а двоичные числа начинаются с 0b.
Максимальное значение для 8-разрядного беззнакового целого составляет 255 (0xFF или 0b11111111).
Если вы добавите 1, вы обычно ожидаете получить: 256 (0x100 или 0b100000000).
Но так как это слишком много бит (9), что по max, поэтому первая часть просто выпадает, что дает вам эффективно 0 (0x (1) 00 или 0b (1) 00000000, но с 1 падением).
Итак, когда ваша программа запускается, вы получаете:
1 = 0x01 = 0b1
2 = 0x02 = 0b10
4 = 0x04 = 0b100
8 = 0x08 = 0b1000
16 = 0x10 = 0b10000
32 = 0x20 = 0b100000
64 = 0x40 = 0b1000000
128 = 0x80 = 0b10000000
256 = 0x00 = 0b00000000 (wraps to 0)
0 + 0 = 0 = 0x00 = 0b00000000
0 + 0 = 0 = 0x00 = 0b00000000
0 + 0 = 0 = 0x00 = 0b00000000
...
Ответ 10
Самый большой десятичный литерал типа int
- 2147483648 (= 2 31). Все десятичные литералы от 0 до 2147483647 могут появляться везде, где может появляться int literal, но литерал 2147483648 может отображаться только как операнд унарного оператора отрицания -.
Если целочисленное добавление переполняется, то результатом являются младшие биты математической суммы, представленные в некотором достаточно большом формате с двумя дополнениями. Если происходит переполнение, знак результата не совпадает с знаком математической суммы двух значений операнда.
Ответ 11
Причина, по которой вы не видите более низкие цифры, состоит в том, что к моменту начала записи на выходе она уже прошла диапазон int, некоторые из-за анализа ABCD/JIT - из-за медленного характера Java...
http://reverseblade.blogspot.com/2009/02/c-versus-c-versus-java-performance.html
Держу пари, если вы добавили барьер памяти или позвонили в "sleep" или объявили int как volatile
, вы увидите больше номеров в нижнем диапазоне.