Как я могу проверить, приведет ли умножение двух чисел в Java к переполнению? - PullRequest
92 голосов
/ 01 ноября 2009

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

int a = 20;
long b = 30;

// if a or b are big enough, this result will silently overflow
long c = a * b;

Это упрощенная версия. В реальной программе a и b получены в другом месте во время выполнения. То, чего я хочу достичь, это что-то вроде этого:

long c;
if (a * b will overflow) {
    c = Long.MAX_VALUE;
} else {
    c = a * b;
}

Как вы предлагаете мне лучше всего это кодировать?

Обновление: a и b всегда неотрицательны в моем сценарии.

Ответы [ 14 ]

1 голос
/ 18 октября 2016

Как уже указывалось, в Java 8 есть методы Math.xxxExact, которые генерируют исключения при переполнении.

Если вы не используете Java 8 для своего проекта, вы все равно можете «позаимствовать» их реализации, которые довольно компактны.

Вот некоторые ссылки на эти реализации на стороннем веб-сайте, нет гарантии того, что они останутся действительными, но в любом случае вы должны иметь возможность зайти в исходный код JDK и посмотреть, как они делают свое чудо в классе java.lang.Math .

Math.multiplyExact(long, long) http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Math.java?av=f#882

Math.addExact http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Math.java?av=f#805

и т. Д. И т. П.

1 голос
/ 02 ноября 2009

Может быть, это поможет вам:

/**
 * @throws ArithmeticException on integer overflow
 */
static long multiply(long a, long b) {
    double c = (double) a * b;
    long d = a * b;

    if ((long) c != d) {
        throw new ArithmeticException("int overflow");
    } else {
        return d;
    }
}
1 голос
/ 01 ноября 2009

Может быть:

if(b!= 0 && a * b / b != a) //overflow

Не уверен насчет этого "решения".

Редактировать: добавлено b! = 0.

Перед тем как понизить голос : a * b / b не будет оптимизировано Это будет ошибка компилятора. Я до сих пор не вижу случая, когда ошибка переполнения может быть замаскирована.

0 голосов
/ 05 декабря 2012

с / с ++ (длинный * длинный):

const int64_ w = (int64_) a * (int64_) b;    
if ((long) (w >> sizeof(long) * 8) != (long) w >> (sizeof(long) * 8 - 1))
    // overflow

java (int * int, извините, я не нашел int64 в java):

const long w = (long) a * (long) b;    
int bits = 32; // int is 32bits in java    
if ( (int) (w >> bits) != (int) (w >> (bits - 1))) {
   // overflow
}

1. сохранить результат в большом типе (int * int переводит результат в long, long * long в int64)

2.cmp результат >> биты и результат >> (биты - 1)

...