почему Integer.MAX_VALUE + 1 == Integer.MIN_VALUE? - PullRequest
25 голосов
/ 22 февраля 2012

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

верно.

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

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

Ответы [ 8 ]

28 голосов
/ 22 февраля 2012

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

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

25 голосов
/ 22 февраля 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
8 голосов
/ 22 февраля 2012

Вы должны понимать, как целочисленные значения представлены в двоичной форме, и как работает двоичное сложение. Java использует представление, называемое дополнением к двум, в котором первый бит числа представляет его знак. Всякий раз, когда вы добавляете 1 к самому большому java Integer, у которого битовый знак равен 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

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

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

7 голосов
/ 22 февраля 2012

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

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

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

4 голосов
/ 22 февраля 2012

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

4 голосов
/ 22 февраля 2012

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

3 голосов
/ 22 февраля 2012

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

0 голосов
/ 29 мая 2019

Причиной переполнения и подсчета двух совместимых кодов идет «второй цикл», мы оказались в крайнем крайнем правом положении 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) `

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...