Подтвердить что ты не робот

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

Возможный дубликат:
Как проверить, может ли перемножение двух чисел в Java вызвать переполнение?

Предположим, что у меня есть метод класса Java, который использует операции * и +.

int foo(int a, int b) {
  ... // some calculations with + and * 
}

Как убедиться, что переполнение не происходит в foo?

Я думаю, я могу либо использовать BigDecimal, либо заменить все + и * на "обертки", например:

int sum(int a, int b) {
   int c = a + b;
   if (a > 0 && b > 0 && c < 0) 
     throw new MyOverfowException(a, b)
   return c;
}

int prod(int a, int b) {
   int c = a * b;
   if (a > 0 && b > 0 && c < 0) 
     throw new MyOverfowException(a, b)
   return c;
}

Есть ли лучшие способы убедиться, что в методе Java не происходит переполнение int?

4b9b3361

Ответ 1

Это сложная проблема с технической точки зрения.

Сайт Безопасное кодирование рекомендует:

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

Эта статья Dr Dobbs предлагает создать библиотеку примитивных арифметических методов, которые выполняют каждую примитивную операцию с явной проверкой переполнения. (Вы могли бы рассмотреть это как реализацию маркерной точки № 2 выше.) Но авторы идут дальше, предлагая вам использовать переработку байт-кода для замены арифметических байт-кодов вызовами эквивалентных методов, которые включают проверки переполнения.

К сожалению, нет возможности включить проверку переполнения в Java. (Но то же самое относится к множеству других языков, например C, С++...)

Ответ 2

Один из способов проверки переполнения состоит в том, чтобы иметь операнды, продвигаемые к большему типу (с удвоенной исходной длиной бит операнда), затем выполнить операцию, а затем посмотреть, слишком ли велико результирующее значение для исходного типа, например

int sum(int a, int b) {
    long r = (long)a + b;
    if (r >>> 32 != 0) {    // no sign extension
        throw new MyOverflowException(a, b);
    }
    return (int)r;
}

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

Ответ 3

Сумма: проверьте, больше ли b больше разницы максимального значения, которое вы можете сохранить в int минус значение a. Если a и/или b могут быть отрицательными, вы должны (i) быть осторожными, чтобы не получить переполнение уже для проверки разницы и (ii) выполнить аналогичную проверку для минимума.

Продукт: это сложнее. Я бы разложил целые числа на два целых числа полуразряд (т.е. Если int 32 бит, разделил его на два 16-битных числа, используя бит-маскирование и смещение). Затем выполните умножение, а затем посмотрите, подходит ли результат в 32 бит.

Все, при условии, что вы не хотите просто взять long для временного результата.

Ответ 4

Предположим, что a и b положительны или отрицательны, и если знак a + b не равен знаку a и b, то происходит переполнение. Это правило можно использовать для определения того, происходит ли переполнение и генерирует исключение. Когда вы поймаете этот эксцесс, вы можете справиться с ним в соответствии с методом, указанным в предыдущих ответах. Другим методом является операция, использующая самый большой тип диапазона, который не будет переполняться. Вы можете использовать long для операции между целыми числами.