Почему я = я + я даю 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 на левой стороне, его значение сбрасывается на 0?

Если бы кто-нибудь мог указать мне на мелкие детали этого, это было бы здорово.

Измените int на long и, кажется, печатать цифры, как и ожидалось. Я удивлен тем, как быстро он достигает максимального 32-битного значения!

96 голосов | спросил DeaIss 12 J0000006Europe/Moscow 2014, 02:12:03

8 ответов


0

Проблема связана с целочисленным переполнением.

В 32-битной арифметике с двумя дополнениями:

i действительно начинается с значений степени двойки, но затем начинается переполнение, когда вы переходите к 2 30

  

2 30 + 2 30 = -2 31

     

-2 31 + -2 31 = 0

... в int арифметика.

ответил Louis Wasserman 12 J0000006Europe/Moscow 2014, 02:14:37
0

Введение

Проблема в целочисленном переполнении. Если он переполняется, он возвращается к минимальному значению и продолжает оттуда. Если он опустился, он возвращается к максимальному значению и продолжает оттуда. Изображение ниже одометра. Я использую это, чтобы объяснить переполнение. Это механическое переполнение, но хороший пример все же.

В одометре max digit = 9, поэтому выход за пределы максимума означает 9 + 1, который переносит и дает 0; Однако нет более высокой цифры, которую можно изменить на 1, поэтому счетчик сбрасывается в zero. Вы поняли - на ум приходят «целочисленные переполнения».

введите описание изображения здесьвведите описание изображения здесь

  

Самый большой десятичный литерал типа int - это 2147483647 (2 31 -1). Все   десятичные литералы от 0 до 2147483647 могут появляться где угодно   литерал может появляться, но литерал 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

ответил Ali Gajani 12 J0000006Europe/Moscow 2014, 02:23:56
0
static int i = 1;
    public static void main(String[] args) throws InterruptedException {
        while (true){
            i = i + i;
            System.out.println(i);
            Thread.sleep(100);
        }
    }

вне помещения:

2
4
8
16
32
64
...
1073741824
-2147483648
0
0

when sum > Integer.MAX_INT then assign i = 0;
ответил TrungTran05T3 12 J0000006Europe/Moscow 2014, 13:22:35
0

Поскольку у меня недостаточно репутации, я не могу опубликовать изображение вывода для той же программы на C с контролируемым выводом, вы можете попробовать сами и увидеть, что он на самом деле печатает 32 раза, а затем, как объясняется из-за переполнения > i = 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;
}
ответил Kaify 12 J0000006Europe/Moscow 2014, 18:55:46
0

Значение i хранится в памяти с использованием фиксированного количества двоичных цифр. Когда для номера требуется больше цифр, чем доступно, сохраняются только самые младшие цифры (самые высокие цифры теряются).

Добавление i к себе аналогично умножению i на два. Так же, как умножение числа на десять в десятичной записи можно выполнить, сдвинув каждую цифру влево и поместив ноль справа, умножение числа на две в двоичной записи может быть выполнено таким же образом. Это добавляет одну цифру справа, поэтому цифра теряется слева.

Здесь начальное значение равно 1, поэтому, если мы используем 8 цифр для хранения i (например),

  • после 0 итераций значение равно 00000001
  • после 1 итерации значение равно 00000010
  • после 2 итераций значение равно 00000100

и так далее, до последнего ненулевого шага

  • после 7 итераций значение равно 10000000
  • после 8 итераций значение равно 00000000

Независимо от того, сколько двоичных цифр выделено для хранения номера, и независимо от того, каково начальное значение, в конечном итоге все цифры будут потеряны, поскольку они сдвинуты влево. После этой точки продолжение удвоения числа не изменит число - оно все равно будет представлено всеми нулями.

ответил starchild 13 J0000006Europe/Moscow 2014, 02:30:04
0

Для отладки таких случаев полезно сократить количество итераций в цикле. Используйте это вместо своего while(true):

for(int r = 0; r<100; r++)

Затем вы можете увидеть, что он начинается с 2 и удваивает значение, пока не вызовет переполнение.

ответил user3732069 12 J0000006Europe/Moscow 2014, 03:02:14
0

Я буду использовать 8-битное число для иллюстрации, потому что оно может быть полностью детализировано за короткий промежуток времени. Шестнадцатеричные числа начинаются с 0x, а двоичные числа начинаются с 0b.

Максимальное значение для 8-разрядного целого числа без знака составляет 255 (0xFF или 0b11111111). Если вы добавите 1, вы, как правило, ожидаете получить: 256 (0x100 или 0b100000000). Но так как это слишком много битов (9), это больше максимума, поэтому первая часть просто отбрасывается, оставляя вам эффективно 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
...
ответил rich remer 15 J0000006Europe/Moscow 2014, 21:31:03
0

Самый большой десятичный литерал типа int равен 2147483648 (= 2 31 ). Все десятичные литералы от 0 до 2147483647 могут появляться везде, где может появляться литерал int, но литерал 2147483648 может появляться только как операнд унарного оператора отрицания -.

Если целочисленное сложение переполняется, то результатом являются младшие биты математической суммы, представленные в некотором достаточно большом формате с двумя дополнениями. Если происходит переполнение, то знак результата не совпадает со знаком математической суммы двух значений операнда.

ответил Scooba doo 12 J0000006Europe/Moscow 2014, 15:44:10

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132