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

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

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

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

4b9b3361

Ответ 1

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

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

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(), который выкинет 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.

Ответ 2

Ну, что касается примитивных целых типов, 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 limits
    return (int) result;
}

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


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

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

/**
 * Add two int 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 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) отрицательны и результат положительный. Логика в значительной степени эквивалентна положительному случаю; все битовые шаблоны, которые могут возникнуть в результате добавления двух отрицательных значений, будут иметь бит знака, очищенный, если и только если произошло недополнение.

Ответ 3

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;

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

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

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

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

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

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

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

Ответ 4

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

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

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

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

Ответ 5

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

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;
}

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

Ответ 6

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

Ответ 7

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

например:

public class Test {

    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

Ответ 8

Я думаю, вы должны использовать что-то вроде этого, и это называется 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;
}

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

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

Ответ 9

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

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

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

Ответ 10

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);
}

Ответ 11

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

static boolean addWillOverFlow(int a, int b) {
    return (Integer.signum(a) == Integer.signum(b)) && 
            (Integer.signum(a) != Integer.signum(a+b)); 
}