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