Почему я = я + я дает мне 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 hereenter 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, вы увидите больше номеров в нижнем диапазоне.