почему Integer.MAX_VALUE + 1 == Integer.MIN_VALUE?

System.out.println(Integer.MAX_VALUE + 1 == Integer.MIN_VALUE);

правда.

Я понимаю, что целое число в Java 32-битное и не может превышать 231-1, но я не могу понять, почему добавление 1 к его MAX_VALUE приводит к MIN_VALUE, а не к какому-то исключению. Не говоря уже о прозрачном преобразовании в больший тип, как это делает Ruby.

Это поведение где-то указано? Могу ли я на это положиться?


person Oleg Mikheev    schedule 22.02.2012    source источник
comment
Здесь сейчас много хороших ответов, однако мне теперь любопытно, почему вы спрашиваете, могу ли я на это положиться? - Почему вы хотите полагаться на него?   -  person Brian    schedule 22.02.2012
comment
это может быть другой вопрос, --- я искал какой-нибудь "итератор", который бы перебирал массив, начиная с элемента N, затем после достижения конечного элемента начинал итерацию от элемента 0 до элемента N-1 --- и это целочисленное поведение может быть полезно для этого... но в итоге я получил только два разных цикла   -  person Oleg Mikheev    schedule 22.02.2012
comment
Для справки: если вместо этого вы хотите получить исключение, некоторые библиотеки предоставляют методы: Guava Apache   -  person Louis Wasserman    schedule 22.02.2012
comment
Что еще больше сбивает с толку, так это то, почему инверсия минимального значения INT по-прежнему отрицательная! int b = -Integer.MIN_VALUE; !! Математика дополнения 2 настолько запутана.   -  person djangofan    schedule 04.12.2016
comment
Все эти ответы, все о том, как работает int, ни один из них не отвечает на вопрос: почему он это делает? Почему не выбрасывает исключение? Есть ли какие-то полезные функции, которые можно получить, разрешив Integer.MAX_VALUE + 1 == -2147483648?   -  person bobanahalf    schedule 17.10.2018


Ответы (9)


Потому что целое переполняется. Когда он переполняется, следующим значением будет Integer.MIN_VALUE. Соответствующий JLS

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

person Bozho    schedule 22.02.2012

Целочисленное хранилище переполняется и это никак не указывается , как указано в JSL 3rd Ed.:

Встроенные целочисленные операторы никоим образом не указывают на переполнение или потерю значимости. Целочисленные операторы могут выдавать NullPointerException при преобразовании распаковки (§5.1. 8) нулевой ссылки. Помимо этого, единственные целочисленные операторы, которые могут генерировать исключение, (§11) — оператор целочисленного деления / (§15.17.2) и оператор целочисленного остатка % (§15.17.3), которые выдают ArithmeticException, если правый операнд равен нулю, и операторы инкремента и декремента ++(§15.15.1, §15.15.2) и --(§15.14.3, §15.14.2), который может выдать OutOfMemoryError, если преобразование упаковки (§5.1.7) требуется, но для выполнения преобразования недостаточно памяти.

Пример в 4-битном хранилище:

MAX_INT: 0111 (7)
MIN_INT: 1000 (-8)

MAX_INT + 1:

 0111+
 0001
 ----
 1000
person falsarella    schedule 22.02.2012
comment
Все эти ответы, все о том, как работает int, ни один из них не отвечает на вопрос: почему он это делает? Почему не выбрасывает исключение? Есть ли какие-то полезные функции, которые можно получить, разрешив Integer.MAX_VALUE + 1 == -2147483648? - person bobanahalf; 17.10.2018

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

Эта ссылка объясняет более подробно: http://www.cs.grinnell.edu/~rebelsky/Espresso/Readings/binary.html#integers-in-java

--

Спецификация языка Java рассматривает это поведение здесь: http://docs.oracle.com/javase/specs/jls/se6/html/expressions.html#15.18.2

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

Это означает, что вы можете положиться на это поведение.

person ARRG    schedule 22.02.2012

На большинстве процессоров арифметические инструкции не имеют режима отказа при переполнении. Они устанавливают флаг, который необходимо проверить. Это дополнительная инструкция, поэтому, вероятно, медленнее. Чтобы языковые реализации были максимально быстрыми, языки часто задаются так, чтобы игнорировать ошибку и продолжать работу. Для Java поведение указано в JLS. Для C язык не определяет поведение, но современные процессоры будут вести себя как Java.

Я считаю, что есть предложения (неудобные) библиотеки Java SE 8 для переполнения, а также неподписанные операции. Я считаю, что поведение, популярное в мире DSP, заключается в фиксации значений на максимумах, поэтому Integer.MAX_VALUE + 1 == Integer.MAX_VALUE [не Java].

Я уверен, что будущие языки будут использовать целые числа произвольной точности, но пока не скоро. Для быстрой работы требуется более дорогой дизайн компилятора.

person Tom Hawtin - tackline    schedule 22.02.2012
comment
Это единственный ответ, который законно отвечает на фактический вопрос. - person inavda; 18.01.2019

Та же причина, по которой дата меняется, когда вы пересекаете международную линию перемены дат: там разрыв. Это встроено в природу двоичного сложения.

person duffymo    schedule 22.02.2012
comment
Это правда, что существует разрыв (или, другими словами, границы диапазона int) и что с этим нужно каким-то образом справиться. Но из этого не следует, что это должно обрабатываться таким образом. Существует ряд других способов обработки целочисленного переполнения... по крайней мере, теоретически. - person Stephen C; 15.04.2021

Это хорошо известная проблема, связанная с тем, что целые числа представлены как дополнение до двух вниз на двоичном уровне. Когда вы добавляете 1 к максимальному значению числа в дополнении до двух, вы получаете минимальное значение. Честно говоря, все целые числа вели себя так до появления Java, и изменение этого поведения для языка Java добавило бы больше накладных расходов в математику целых чисел и смутило бы программистов, пришедших с других языков.

person Craig H    schedule 22.02.2012

Когда вы добавляете 3 (в двоичном формате 11) к 1 (в двоичном формате 1), вы должны изменить на 0 (в двоичном формате 0) все двоичные числа 1, начиная справа, пока не получите 0, который вы должны изменить на 1. В Integer.MAX_VALUE все места заполнены 1, поэтому осталось только 0.

person IProblemFactory    schedule 22.02.2012

Легко понять с помощью байтового примера => введите здесь описание изображения

byte a=127;//max value for byte
byte b=1;

byte c=(byte) (a+b);//assigns -128
System.out.println(c);//prints -128

Здесь мы форсируем сложение и приводим его к обработке как байта. Итак, что произойдет, когда мы достигнем 127 (максимально возможное значение для байта) и добавим плюс 1, тогда значение изменится (как показано на рисунке) со 127 и станет -128. Значение начинает кружиться вокруг типа.

То же самое для целого числа.

Кроме того, целое число + целое число остается целым числом (в отличие от байта + байта, которое преобразуется в целое [если не выполняется принудительное преобразование, как указано выше]).

int int1=Integer.MAX_VALUE+1;
System.out.println(int1); //prints -2147483648
System.out.println(Integer.MIN_VALUE); //prints -2147483648

//below prints 128 as converted to int as not forced with casting
System.out.println(Byte.MAX_VALUE+1);
person Chirag    schedule 14.04.2021
comment
Материал о добавлении byte арифметики немного вводит в заблуждение ... и не совсем актуален. Поскольку значения byte всегда повышаются до int (или более крупного типа) перед выполнением над ними каких-либо арифметических операций, обсуждение примеров с участием byte не поможет понять поведение целочисленного переполнения. - person Stephen C; 15.04.2021

Потому что переполнение и двойной подсчет природы идут по «второму циклу», мы были в крайнем правом положении 2147483647, и после суммирования 1 мы оказались в крайнем левом положении -2147483648, следующее увеличение идет -2147483647, -2147483646, -2147483645, ... и так далее до крайнего правого снова и снова и снова, его природа суммирующей машины на этой разрядности.

Несколько примеров:

int a = 2147483647;

System.out.println(a);

дает: 2147483647

System.out.println(a+1);

дает: -2147483648 (поскольку переполнение и двойной подсчет природы идут по «второму циклу», мы были в крайнем правом положении 2147483647, и после суммирования 1 мы оказались в крайнем левом положении -2147483648, следующее увеличение идет -2147483648, - 2147483647, -2147483646, ... и так далее до самого правого снова и снова и снова, его природа суммирующей машины на этой разрядности)

System.out.println(2-a); 

дает: -2147483645 (-2147483647+2 кажется математически логичным)

System.out.println(-2-a);

дает: 2147483647 (-2147483647-1 -> -2147483648, -2147483648-1 -> 2147483647 какой-то цикл, описанный в предыдущих ответах)

System.out.println(2*a);

дает: -2 (2147483647+2147483647 -> -2147483648+2147483646 снова математический логический)

System.out.println(4*a);

дает: -4 (2147483647+2147483647+2147483647+2147483647 -> -2147483648+2147483646+2147483647+2147483647 -> -2-2 (согласно последнему ответу) -> -4)`

person Oleg Kamuz    schedule 29.05.2019