Проверка переполнения и диапазона арифметических операций

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

скажите upperbound для краткости в java 32767, и я умножаю 328 * 100, я не могу на самом деле сделать сравнение с Short.MAX_VALUE потому что после умножения ответ уже будет переполнен, и ответ будет -32736, который, безусловно, меньше, чем Short.MAX_VALUE

возьмите другой пример, скажем, я int значение вычислять 17^10 (17 к силе 10) в цикле for. Откуда мне знать, на каком этапе мой ответ был переполнен.

этой Short и int это просто пример. Думаю, эта проблема в большей проницательности, что именно можно сделать для всех типов данных.

Я попытался погуглить, но не нашел хорошей ссылки, которая помогает понять концепцию.

3 ответов


есть план включить такие методы в пакет Math в Java 8, но я не знаю, каково текущее состояние. Некоторый исходный код доступен здесь. Я не знаю, насколько проверена реализация, но это может дать вам идеи.

например, умножение int выполняется с помощью longs:

public static int multiplyExact(int x, int y) {
    long r = (long)x * (long)y;
    if ((int)r != r) {
        throw new ArithmeticException("long overflow");
    }
    return (int)r;
}

но длинное умножение использует более сложный алгоритм:

public static long multiplyExact(long x, long y) {
    long r = x * y;
    long ax = Math.abs(x);
    long ay = Math.abs(y);
    if (((ax | ay) >>> 31 != 0)) {
        // Some bits greater than 2^31 that might cause overflow
        // Check the result using the divide operator
        // and check for the special case of Long.MIN_VALUE * -1
       if (((y != 0) && (r / y != x)) ||
            (x == Long.MIN_VALUE && y == -1)) {
            throw new ArithmeticException("long overflow");
        }
    }
    return r;
}  

существует 3 возможных метода проверки переполнения:

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

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

например:

static void preAddCheck(int left, int right) throws ArithmeticException {
   if (right > 0 ? left > Integer.MAX_VALUE - right : left < Integer.MIN_VALUE - right) {
    throw new ArithmeticException("Integer overflow");
  }
}

BigInteger: преобразуйте входные данные в объекты типа BigInteger и выполняйте всю арифметику с помощью методов BigInteger. ArithmeticException брошен на переполнение.


Я бы сделал расчет, используя максимально возможный тип BigInteger / BigDecimal. Затем я бы присвоил значение соответствующему типу в зависимости от его величины... Интересно, что для этого есть несколько полезных методов... shortValueExtract выдаст исключение ArithmetricException, если значение не может содержаться в коротком..

BigDecimal result = BigDecimal.valueOf(328).multiply(
        BigDecimal.valueOf(100));
try {
    short shortResult = result.shortValueExact();
} catch (ArithmeticException e) {
    // overflow
    System.out.println("Overflow!");
}

try {
    int intResult = result.intValueExact();
} catch (ArithmeticException e) {
    // overflow
}