Как Java обрабатывает целое число с переполнением и переполнением и как вы его проверите?

Как Java обрабатывает целочисленные streamи и переполнения?

Исходя из этого, как бы вы проверить / проверить, что это происходит?

Если он переполняется, он возвращается к минимальному значению и продолжается оттуда. Если он переполняется, он возвращается к максимальному значению и продолжается оттуда.

Вы можете проверить это заранее следующим образом:

 public static boolean willAdditionOverflow(int left, int right) { if (right < 0 && right != Integer.MIN_VALUE) { return willSubtractionOverflow(left, -right); } else { return (~(left ^ right) & (left ^ (left + right))) < 0; } } public static boolean willSubtractionOverflow(int left, int right) { if (right < 0) { return willAdditionOverflow(left, -right); } else { return ((left ^ right) & (left ^ (left - right))) < 0; } } 

(вы можете заменить int на long выполнение тех же самых проверок long )

Если вы считаете, что это может произойти более чем часто, подумайте об использовании типа данных или объекта, который может хранить более крупные значения, например long или, возможно, java.math.BigInteger . Последнее не переполняет, практически, доступная память JVM является пределом.


Если вы уже на Java8 уже, то вы можете использовать новые Math#addExact() и Math#subtractExact() которые будут Math#addExact() ArithmeticException при переполнении.

 public static boolean willAdditionOverflow(int left, int right) { try { Math.addExact(left, right); return false; } catch (ArithmeticException e) { return true; } } public static boolean willSubtractionOverflow(int left, int right) { try { Math.subtractExact(left, right); return false; } catch (ArithmeticException e) { return true; } } 

Исходный код можно найти здесь и здесь, соответственно.

Конечно, вы могли бы просто использовать их сразу же, вместо того чтобы скрывать их в boolean методе утилиты.

Ну, что касается примитивных целых типов, Java не обрабатывает Over / Underflow вообще (для float и double поведение отличается, оно будет выравниваться до +/- бесконечности, как и мандаты IEEE-754).

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

 public int addWithOverflowCheck(int a, int b) { // the cast of a is required, to make the + work with long precision, // if we just added (a + b) the addition would use int precision and // the result would be cast to long afterwards! long result = ((long) a) + b; if (result > Integer.MAX_VALUE) { throw new RuntimeException("Overflow occured"); } else if (result < Integer.MIN_VALUE) { throw new RuntimeException("Underflow occured"); } // at this point we can safely cast back to int, we checked before // that the value will be withing int's limits return (int) result; } 

То, что вы делаете вместо предложений броска, зависит от требований ваших приложений (бросок, сброс до min / max или просто запись любого). Если вы хотите обнаружить переполнение при длительных операциях, вам не повезло с примитивами, вместо этого используйте BigInteger.


Edit (2014-05-21): Поскольку этот вопрос, как представляется, упоминается довольно часто, и мне приходилось решать ту же проблему самостоятельно, довольно легко оценить условие переполнения тем же методом, что и ЦП будет вычислять свой флаг V.

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

 /** * Add two int's with overflow detection (r = s + d) */ public static int add(final int s, final int d) throws ArithmeticException { int r = s + d; if (((s & d & ~r) | (~s & ~d & r)) < 0) throw new ArithmeticException("int overflow add(" + s + ", " + d + ")"); return r; } 

В java его проще применить выражение (в if) ко всем 32 битам и проверить результат с помощью <0 (это будет эффективно проверять бит знака). Принцип работает точно так же для всех целочисленных примитивных типов , изменяя все объявления в вышеописанном методе, чтобы он долго работал.

Для более мелких типов из-за неявного преобразования в int (см. JLS для побитовых операций для деталей) вместо проверки <0, проверка должна явно маскировать знак знака (0x8000 для коротких операндов, 0x80 для операндов байтов, корректировать приведения и объявление параметров соответственно):

 /** * Subtract two short's with overflow detection (r = d - s) */ public static short sub(final short d, final short s) throws ArithmeticException { int r = d - s; if ((((~s & d & ~r) | (s & ~d & r)) & 0x8000) != 0) throw new ArithmeticException("short overflow sub(" + s + ", " + d + ")"); return (short) r; } 

(Обратите внимание, что в приведенном выше примере используется потребность выражения для вычитания переполнения)


Итак, как / почему эти булевы выражения работают? Во-первых, некоторое логическое мышление показывает, что переполнение может произойти только в том случае, если знаки обоих аргументов одинаковы. Поскольку, если один аргумент отрицательный и один положительный, результат (добавления) должен быть ближе к нулю, или в крайнем случае один аргумент равен нулю, так же как и другой аргумент. Поскольку аргументы сами по себе не могут создать условие переполнения, их сумма также не может создать переполнение.

Итак, что произойдет, если оба аргумента имеют один и тот же знак? Давайте взглянем на случай, оба положительные: добавление двух аргументов, которые создают сумму, большую, чем типы MAX_VALUE, всегда будет давать отрицательное значение, поэтому переполнение происходит, если arg1 + arg2> MAX_VALUE. Теперь максимальное значение, которое может получиться, будет MAX_VALUE + MAX_VALUE (в крайнем случае оба аргумента MAX_VALUE). Для байта (пример), который будет означать 127 + 127 = 254. Рассматривая представления бит всех значений, которые могут возникнуть в результате добавления двух положительных значений, можно обнаружить, что для тех, у которых переполнение (от 128 до 254) установлено бит 7, в то время как все, что не переполняется (от 0 до 127), имеют бит 7 (верхний, знак), очищенный. То, что проверяет первая (правая) часть выражения:

 if (((s & d & ~r) | (~s & ~d & r)) < 0) 

(~ s & ~ d & r) становится истинным, только если оба операнда (s, d) положительны, а результат (r) отрицателен (выражение работает на всех 32 битах, но единственный бит, который нам интересен является самым верхним (знаковым) битом, который проверяется на <0).

Теперь, если оба аргумента отрицательны, их сумма никогда не может быть ближе к нулю, чем любой из аргументов, сумма должна быть ближе к минусовой бесконечности. Самое экстремальное значение, которое мы можем произвести, - MIN_VALUE + MIN_VALUE, которое (опять-таки для байтового примера) показывает, что для любого значения диапазона (от -1 до -128) бит знака устанавливается, а любое возможное переполняющее значение (от -129 до -256 ) имеет бит знака, очищенный. Таким образом, знак результата снова показывает условие переполнения. Это то, что левая половина (s & d & ~ r) проверяет случай, когда оба аргумента (s, d) отрицательны и результат положительный. Логика в значительной степени эквивалентна положительному случаю; все битовые шаблоны, которые могут возникнуть в результате добавления двух отрицательных значений, будут иметь бит знака, очищенный, если и только если произошло недополнение.

Java не делает ничего с переполнением целых чисел для типов int или long primitive и игнорирует переполнение с положительными и отрицательными целыми числами.

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

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

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

Иногда отрицательное переполнение ошибочно называется underflow. Underflow – это то, что происходит, когда значение будет ближе к нулю, чем позволяет представление. Underflow происходит в целочисленной арифметике и ожидается. Целочисленное недоисполнение происходит, когда целочисленная оценка будет находиться между -1 и 0 или 0 и 1. Что бы дробный результат усекался до 0. Это нормально и ожидалось с целочисленной арифметикой и не считалось ошибкой. Тем не менее, это может привести к тому, что код выбросит исключение. Одним из примеров является исключение «ArithmeticException: / zero», если результат integer underflow используется в качестве делителя в выражении.

Рассмотрим следующий код:

 int bigValue = Integer.MAX_VALUE; int x = bigValue * 2 / 5; int y = bigValue / x; 

что приводит к тому, что x присваивается 0, а последующая оценка bigValue / x выдает исключение, «ArithmeticException: / by zero» (т.е. деление на ноль), вместо y присваивается значение 2.

Ожидаемый результат для x будет равен 858,993,458, что меньше максимальной стоимости int 2,147,483,647. Однако промежуточный результат оценки Integer.MAX_Value * 2 будет равен 4 294 967 294, что превышает максимальное значение int и составляет -2 в соответствии с целыми представлениями дополнения 2s. Последующая оценка -2 / 5 оценивается в 0, которая присваивается x.

Перестановка выражения для вычисления x в выражение, которое при оценке делит перед умножением следующий код:

 int bigValue = Integer.MAX_VALUE; int x = bigValue / 5 * 2; int y = bigValue / x; 

В результате х присваивается 858 993 458, а у y – 2, что ожидается.

Промежуточный результат от bigValue / 5 составляет 429,496,729, который не превышает максимальное значение для int. Последующая оценка 429 496 729 * 2 не превышает максимальное значение для int, и ожидаемый результат присваивается x. Оценка для y тогда не делит на ноль. Оценки для x и y работают должным образом.

Целочисленные значения Java сохраняются как и ведут себя в соответствии с целыми представлениями, дополняющими 2s. Когда результирующее значение будет больше или меньше максимального или минимального целочисленного значения, вместо этого получается целое значение целого числа 2. В ситуациях, явно не предназначенных для использования поведения дополнения 2s, которое является наиболее обычной целочисленной арифметической ситуацией, полученное значение 2s-дополнения вызовет логику программирования или вычислительную ошибку, как показано в примере выше. Отличная статья в Википедии описывает двоичные целые числа комплиментов 2s: дополнение к двум – Википедия

Существуют методы предотвращения непреднамеренного переполнения целочисленных значений. Techinques можно classифицировать как использование тестов предварительного тестирования, повышения и BigInteger.

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

Upcasting включает использование более простого примитивного типа для выполнения арифметической операции или выражения, а затем определение того, превышает ли полученное значение максимальное или минимальное значения для целого числа. Даже при повышении уровня, все же возможно, что значение или какое-либо промежуточное значение в операции или выражении будет превышать максимальные или минимальные значения для типа upcast и вызвать переполнение, которое также не будет обнаружено и вызовет неожиданные и нежелательные результаты. Благодаря анализу или предварительным условиям может быть возможно предотвратить переполнение с повышением уровня, когда предотrotation без повышения может оказаться невозможным или практичным. Если целые числа являются уже длинными примитивными типами, то восходящий процесс невозможен с примитивными типами в Java.

Метод BigInteger включает использование BigInteger для арифметической операции или выражения с использованием библиотечных методов, которые используют BigInteger. BigInteger не переполняется. При необходимости он будет использовать всю доступную память. Его арифметические методы обычно немного менее эффективны, чем целые операции. По-прежнему возможно, что результат с использованием BigInteger может превышать максимальные или минимальные значения для целого числа, однако переполнение не произойдет в арифметике, ведущей к результату. Программирование и дизайн по-прежнему необходимо будет определить, что делать, если результат BigInteger превышает максимальные или минимальные значения для требуемого примитивного типа результата, например, int или long.

Программа CERT Института программного обеспечения Carnegie Mellon Software Engineering и Oracle создали набор стандартов для безопасного программирования на Java. В стандартах включены методы предотвращения и обнаружения переполнения целого числа. Стандарт публикуется как свободно ansible онлайн-ресурс здесь: CERT Oracle Secure Coding Standard для Java

Раздел стандарта, который описывает и содержит практические примеры методов кодирования для предотвращения или обнаружения переполнения целого числа, приведен здесь: NUM00-J. Обнаружение или предотrotation переполнения целых чисел

Также доступны форма для книг и форма PDF стандарта CERT Oracle Secure Coding Standard для Java.

По умолчанию int и long math Java молча обертываются при переполнении и streamе. (Целочисленные операции над другими целыми типами выполняются путем первого продвижения операндов до int или long, для JLS 4.2.2 .)

Начиная с Java 8, java.lang.Math предоставляет addExact , subtractExact , multiplyExact , incrementExact , negateExact и negateExact статические методы как для int, так и для длинных аргументов, которые выполняют именованную операцию, бросая ArithmeticException при переполнении. (Нет метода divideExact – вам нужно будет проверить один специальный случай ( MIN_VALUE / -1 ) самостоятельно).

Начиная с Java 8, java.lang.Math также предоставляет toIntExact чтобы toIntExact long в int, выбрасывая ArithmeticException, если значение long не соответствует int. Это может быть полезно, например, для вычисления суммы int с использованием непроверенной длинной математики, а затем с помощью toIntExact для toIntExact в int в конце (но будьте осторожны, чтобы не переполнять вашу сумму).

Если вы все еще используете более старую версию Java, Google Guava предоставляет статические методы IntMath и LongMath для проверки сложения, вычитания, умножения и экспоненциальности (переполнение). Эти classы также предоставляют методы для вычисления факториалов и биномиальных коэффициентов, которые возвращают MAX_VALUE при переполнении (что менее удобно проверять). В примитивных classах Guava, SignedBytes , UnsignedBytes , Shorts и Ints предусмотрены методы checkedCast для сужения больших типов (бросание IllegalArgumentException при checkedCast / переполнении, а не в ArithmeticException), а также методы saturatingCast которые возвращают MIN_VALUE или MAX_VALUE при переполнении.

Имея просто любопытное совпадение с этой проблемой, вот мое решение (как для умножения, так и для добавления):

 static boolean wouldOverflowOccurwhenMultiplying(int a, int b) { // If either a or b are Integer.MIN_VALUE, then multiplying by anything other than 0 or 1 will result in overflow if (a == 0 || b == 0) { return false; } else if (a > 0 && b > 0) { // both positive, non zero return a > Integer.MAX_VALUE / b; } else if (b < 0 && a < 0) { // both negative, non zero return a < Integer.MAX_VALUE / b; } else { // exactly one of a,b is negative and one is positive, neither are zero if (b > 0) { // this last if statements protects against Integer.MIN_VALUE / -1, which in itself causes overflow. return a < Integer.MIN_VALUE / b; } else { // a > 0 return b < Integer.MIN_VALUE / a; } } } boolean wouldOverflowOccurWhenAdding(int a, int b) { if (a > 0 && b > 0) { return a > Integer.MAX_VALUE - b; } else if (a < 0 && b < 0) { return a < Integer.MIN_VALUE - b; } return false; } 

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

Существуют библиотеки, которые обеспечивают безопасные арифметические операции, которые проверяют переполнение / недополнение целых чисел. Например, IntMath.checkedAdd (int a, int b) Guava возвращает сумму a и b , если она не переполняется, и выбрасывает ArithmeticException если a + b переполняется в подписанной арифметике int .

Я думаю, вы должны использовать что-то вроде этого, и это называется Upcasting:

 public int multiplyBy2(int x) throws ArithmeticException { long result = 2 * (long) x; if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE){ throw new ArithmeticException("Integer overflow"); } return (int) result; } 

Вы можете прочитать далее: Обнаружение или предотrotation переполнения целых чисел

Это довольно надежный источник.

Он обертывается.

например:

публичный тест classа {

 public static void main(String[] args) { int i = Integer.MAX_VALUE; int j = Integer.MIN_VALUE; System.out.println(i+1); System.out.println(j-1); } 

}

печать

-2147483648

2147483647

Он ничего не делает – происходит переполнение / переполнение.

«-1», который является результатом переполнения вычислений, ничем не отличается от «-1», который является результатом любой другой информации. Таким образом, вы не можете сказать через какой-либо статус или путем проверки только значения, было ли оно переполнено.

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

 static final int safeAdd(int left, int right) throws ArithmeticException { if (right > 0 ? left > Integer.MAX_VALUE - right : left < Integer.MIN_VALUE - right) { throw new ArithmeticException("Integer overflow"); } return left + right; } static final int safeSubtract(int left, int right) throws ArithmeticException { if (right > 0 ? left < Integer.MIN_VALUE + right : left > Integer.MAX_VALUE + right) { throw new ArithmeticException("Integer overflow"); } return left - right; } static final int safeMultiply(int left, int right) throws ArithmeticException { if (right > 0 ? left > Integer.MAX_VALUE/right || left < Integer.MIN_VALUE/right : (right < -1 ? left > Integer.MIN_VALUE/right || left < Integer.MAX_VALUE/right : right == -1 && left == Integer.MIN_VALUE) ) { throw new ArithmeticException("Integer overflow"); } return left * right; } static final int safeDivide(int left, int right) throws ArithmeticException { if ((left == Integer.MIN_VALUE) && (right == -1)) { throw new ArithmeticException("Integer overflow"); } return left / right; } static final int safeNegate(int a) throws ArithmeticException { if (a == Integer.MIN_VALUE) { throw new ArithmeticException("Integer overflow"); } return -a; } static final int safeAbs(int a) throws ArithmeticException { if (a == Integer.MIN_VALUE) { throw new ArithmeticException("Integer overflow"); } return Math.abs(a); } 

Существует один случай, который не упоминается выше:

 int res = 1; while (res != 0) { res *= 2; } System.out.println(res); 

будет производить:

 0 

Этот случай обсуждался здесь: Integer overflow производит Zero.

Я думаю, это должно быть хорошо.

 static boolean addWillOverFlow(int a, int b) { return (Integer.signum(a) == Integer.signum(b)) && (Integer.signum(a) != Integer.signum(a+b)); } 
  • Как Java-интерфейсы моделируют множественное наследование?
  • Как изменить стиль стрелки в JComboBox
  • Перестановка массива
  • Защищенный конструктор и доступность
  • Лучший способ писать байты в середине файла в Java
  • Установка параметра в виде списка для выражения IN
  • Регистрация активности пользователя в веб-приложении
  • Ограничение максимального размера HashMap в Java
  • Ограничение ли Java на длину имени classа?
  • API JavaMail для iMail - java.net.SocketException: Permission denied: connect
  • Если частные вспомогательные методы являются статическими, если они могут быть статическими
  • Давайте будем гением компьютера.