Инт проверка переполнения в Java


У меня есть кусок кода, который берет пары чисел и проверить, если выполнение сложения на входах приведет к переполнению.

Мне было интересно, если этот код является твердой:

public static boolean CanAdd(int me, int... args) { 
    int total = me;
    for (int arg : args) {
        if (total >= 0) {
            if (java.lang.Integer.MAX_VALUE - total >= arg) { // since total is positive, (MaxValue - total) will never overflow
                total += arg;
            } else {
                return false;
            }
        } else {
            if (java.lang.Integer.MIN_VALUE- total <= arg) { // same logic as above
                total += arg;
            } else {
                return false;
            }
        }
    }
    return true;
}

У кого-нибудь есть лучше (быстрее) способ достичь того же?



23059
29
задан 24 ноября 2011 в 03:11 Источник Поделиться
Комментарии
3 ответа

Я не нашел каких-либо материалов, которые не обработаны в коде. Вот некоторые тесты:

assertTrue(CanAdd(0, Integer.MAX_VALUE));
assertTrue(CanAdd(0, Integer.MIN_VALUE));
assertTrue(CanAdd(Integer.MIN_VALUE, 0));
assertTrue(CanAdd(-1, Integer.MAX_VALUE));
assertFalse(CanAdd(1, Integer.MAX_VALUE));
assertFalse(CanAdd(-1, Integer.MIN_VALUE));

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

public static boolean canAdd(int... values) {
long sum = 0;
for (final int value: values) {
sum += value;
if (sum > Integer.MAX_VALUE) {
return false;
}
if (sum < Integer.MIN_VALUE) {
return false;
}
}
return true;
}

Я думаю, что это легче читать и поддерживать.

Наконец, обратите внимание: согласно соглашения о коде на языке программирования Java имя Ваш метод должен быть canAdd (со строчной первой буквой).


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

Редактировать:

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

public static int addAndCheck(int x, int y)
throws MathArithmeticException {
long s = (long)x + (long)y;
if (s < Integer.MIN_VALUE || s > Integer.MAX_VALUE) {
throw new MathArithmeticException(LocalizedFormats.OVERFLOW_IN_ADDITION, x, y);
}
return (int)s;
}

А также гуава:

public static int checkedAdd(int a, int b) {
long result = (long) a + b;
checkNoOverflow(result == (int) result);
return (int) result;
}

19
ответ дан 24 ноября 2011 в 07:11 Источник Поделиться

О текущий код:


  • Я бы переименовать CanAdd в canAdd (в соответствии с кодировкой конвенций).

  • Переименуйте меня в стоимости (это более описательный), и аргументы к ценностям и арг на текущий значение.

  • Удалить ненужные Ява.Лэнг пакет префикс.

public static boolean canAdd(int value, int... values) {
int total = value;
for (int currentValue: values) {
if (total >= 0) {
// since total is positive, (MaxValue - total) will never
// overflow
if (Integer.MAX_VALUE - total >= currentValue) {
total += currentValue;
} else {
return false;
}
} else {
// same logic as above
if (Integer.MIN_VALUE - total <= currentValue) {
total += currentValue;
} else {
return false;
}
}
}
return true;
}

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

Мне не очень нравится значение и значения, вот так я изменил первые две строки немного:

public static boolean canAdd(int... values) {
int total = 0;
...
}

Если вы инвертировать внутренний , если заявления, которые вы могли бы устранить еще ключевые слова:

if (total >= 0) {
if (Integer.MAX_VALUE - total < currentValue) {
return false;
}
total += currentValue;
} else {
if (Integer.MIN_VALUE - total > currentValue) {
return false;
}
total += currentValue;
}

В += В является одинаковой в обеих отраслей, поэтому он может быть перемещен после того, если:

if (total >= 0) {
if (Integer.MAX_VALUE - total < currentValue) {
return false;
}
} else {
if (Integer.MIN_VALUE - total > currentValue) {
return false;
}
}
total += currentValue;

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

 public static boolean canAdd(int... values) {
int total = 0;
for (int currentValue: values) {
final boolean positiveTotal = total >= 0;
if (positiveTotal && (Integer.MAX_VALUE - total < currentValue)) {
return false;
}
if (!positiveTotal && (Integer.MIN_VALUE - total > currentValue)) {
return false;
}
total += currentValue;
}
return true;
}

Но я думаю, это все еще трудно понять. Я поеду с длинным преобразования.

4
ответ дан 25 февраля 2014 в 07:02 Источник Поделиться

Ваша логика выглядит солидно для меня. Это хотя и тонкая,.

Вот еще одна версия, используя длинные, но с намного более простой логики:

public static boolean canAdd(int... values) {
long longSum = 0;
int intSum = 0;
for (final int value: values) {
intSum += value;
longSum += value;
}
return intSum == longSum;
}

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

(6 лет спустя) вот обновленная версия, вдохновленная пользователя 'cellepo', который останавливается, как только он обнаруживает переполнение, во избежание ложных срабатываний (возможно в более ранней версии, если список значений был в миллиардах):

public static boolean canAdd(int... values) {
long longSum = 0;
int intSum = 0;
for (final int value: values) {
intSum += value;
longSum += value;
if (intSum != longSum)
return false;
}
return true;
}

3
ответ дан 30 января 2012 в 12:01 Источник Поделиться