Как Java обрабатывает целое число и переполняет и как бы вы проверили на него?

StackOverflow https://stackoverflow.com/questions/3001836

  •  25-09-2019
  •  | 
  •  

Вопрос

Как Java обрабатывает целое число и переполняет?

Надя от этого, как бы вы проверили / проверили, что это происходит?

Это было полезно?

Решение

Если он переполняется, он возвращается к минимальное значение И продолжается оттуда. Если он придерживается, он возвращается к Максимальное значение И продолжается оттуда.

Вы можете проверить это заранее следующим образом:

public static boolean willAdditionOverflow(int left, int right) {
    if (right < 0 && right != Integer.MIN_VALUE) {
        return willSubtractionOverflow(left, -right);
    } else {
        return (~(left ^ right) & (left ^ (left + right))) < 0;
    }
}

public static boolean willSubtractionOverflow(int left, int right) {
    if (right < 0) {
        return willAdditionOverflow(left, -right);
    } else {
        return ((left ^ right) & (left ^ (left - right))) < 0;
    }
}

(Вы можете заменить int к long выполнять те же проверки для long)

Если вы думаете, что это может произойти более часто, затем рассмотрите возможность использования данных DataType или объекта, который может хранить большие значения, например, long или, может быть java.math.BigInteger. Отказ Последний не переполняется практически, доступная память JVM является предел.


Если вы уже произошли на Java8, то вы можете использовать новый Math#addExact() а также Math#subtractExact() методы, которые бросят ArithmeticException на переполнении.

public static boolean willAdditionOverflow(int left, int right) {
    try {
        Math.addExact(left, right);
        return false;
    } catch (ArithmeticException e) {
        return true;
    }
}

public static boolean willSubtractionOverflow(int left, int right) {
    try {
        Math.subtractExact(left, right);
        return false;
    } catch (ArithmeticException e) {
        return true;
    }
}

Исходный код можно найти здесь а также здесь соответственно.

Конечно, вы также можете просто использовать их сразу, а не скрывать их в boolean Метод полезности.

Другие советы

Ну, насколько примитивно целочисленные типы идут, Java не обрабатывает / недовольно (для float и двойное поведение отличается, он будет промывать до +/- бесконечности так же, как мандаты IEEE-754).

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

public int addWithOverflowCheck(int a, int b) {
    // the cast of a is required, to make the + work with long precision,
    // if we just added (a + b) the addition would use int precision and
    // the result would be cast to long afterwards!
    long result = ((long) a) + b;
    if (result > Integer.MAX_VALUE) {
         throw new RuntimeException("Overflow occured");
    } else if (result < Integer.MIN_VALUE) {
         throw new RuntimeException("Underflow occured");
    }
    // at this point we can safely cast back to int, we checked before
    // that the value will be withing int's limits
    return (int) result;
}

То, что вы будете делать на месте броска, зависит от требований ваших приложений (бросок, промывать до мин / макс или просто войти в систему). Если вы хотите обнаружить переполнение по длительным операциям, вам не повезло с примитивами, вместо этого используйте BigInteger.


Редактировать (2014-05-21): Поскольку этот вопрос, по-видимому, упоминается довольно часто, и мне пришлось решить ту же проблему самостоятельно, ее довольно легко оценить условие переполнения одним и тем же методом, который рассчитывает свой флаг V.

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

/**
 * Add two int's with overflow detection (r = s + d)
 */
public static int add(final int s, final int d) throws ArithmeticException {
    int r = s + d;
    if (((s & d & ~r) | (~s & ~d & r)) < 0)
        throw new ArithmeticException("int overflow add(" + s + ", " + d + ")");    
    return r;
}

В Java его проще, чтобы применить выражение (в IF) на все 32 бита и проверьте результат, используя <0 (это эффективно проверяет знак знаки). Принцип работает точно так же для Все целочисленные примитивные типы, Изменение всех деклараций в вышеупомянутом способе длиннее заставляет его работать долго.

Для более мелких типов, из-за неявного преобразования в int (см. JLS для побитовых операций для получения подробной информации), вместо проверки <0, чек необходимо явно замаскировать знак (0x8000 для коротких операндов, 0x80 для байтовных операндов, отрегулируйте отливки и объявление параметра соответствующим образом):

/**
 * Subtract two short's with overflow detection (r = d - s)
 */
public static short sub(final short d, final short s) throws ArithmeticException {
    int r = d - s;
    if ((((~s & d & ~r) | (s & ~d & r)) & 0x8000) != 0)
        throw new ArithmeticException("short overflow sub(" + s + ", " + d + ")");
    return (short) r;
}

(Обратите внимание, что выше пример использует выражение необходимости для вычесть Обнаружение переполнения)


Так как / почему эти логические выражения работают? Во-первых, некоторое логическое мышление показывает, что переполнение может Только происходят, если признаки обоих аргументов одинаковы. Потому что, если один аргумент отрицательный и один положительный, результат (добавления) должен быть ближе к нулю, или в крайнем случае один аргумент равен нулю, так же, как и другой аргумент. Так как аргументы сами по себе неверно Создайте условие переполнения, их сумма также не может создать переполнение.

Так что происходит, если оба аргумента имеют одинаковый знак? Давайте посмотрим на случай, оба являются положительными: добавление двух аргументов, которые создают сумму, превышающую типы max_value, всегда будет давать отрицательное значение, поэтому происходит переполнение если arg1 + arg2> max_value. Теперь максимальное значение, которое может привести к Max_Value + max_Value (крайний случай оба аргумента - max_value). Для байта (пример), который будет означать 127 + 127 = 254. Глядя на битные представления всех значений, которые могут быть результатом добавления двух положительных значений, можно найти, что те, которые переполняют (от 128 до 254), имеют бит 7 набор, пока Все, что не переполняет (от 0 до 127), имеют бит 7 (самого верхнего, знака) очищены. Это именно то, что первая (правая) частью выражения проверяет:

if (((s & d & ~r) | (~s & ~d & r)) < 0)

(~ S & ~ D & R) становится правдой, только если, обе операнды (S, D) являются положительными, и результат (r) отрицательный (выражение работает на всех 32 битах, но единственное, которое мы заинтересованы в том, - самый верхний (знак), который проверяется на <0).

Теперь, если оба аргумента отрицательны, их сумма никогда не может быть ближе к нулю, чем любой из аргументов, сумма должен быть ближе к минусной бесконечности. Наиболее экстремальное значение, которое мы можем производить, это min_value + min_value, который (опять же для примера байта) показывает, что для любого значения диапазона (-1 до -128) установлено значение знака, в то время как любое возможное переполнение (-129 к -256 ) Имеет знакомый бит очищен. Таким образом, знак результата снова раскрывает состояние переполнения. Это то, что левая половина (S & D & ~ R) проверяет на случай, когда оба аргумента (S, d) отрицательны, и результат, который положительный. Логика в значительной степени эквивалентна положительному случаю; Все шаблоны битов, которые могут привести к добавлению двух отрицательных значений, будет очищен знаки если и только если произошло подолешение.

Java ничего не делает с целочисленным переполнением для INT или длинных примитивных типов и игнорирует переполнение с положительными и отрицательными целыми числами.

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

Целочисленные арифметические и выражения решающих в неожиданном или необнаруженном переполнении являются общая ошибка программирования. Неожиданный или необъясненный целочисленный переполнение также является хорошо известной эксплуатацииющую проблему безопасности, особенно как она влияет на объекты Array, Stack и List.

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

Иногда отрицательный переполнение по ошибке называется olderflow. Inlook - это то, что происходит, когда значение будет ближе к нулю, чем представление позволяет. Inlovflow происходит в целочисленной арифметике и ожидается. Integer inlovflow происходит, когда целочисленная оценка будет между -1 и 0 или 0 и 1. Что будет дробным результатом, усеченным до 0. Это нормально и ожидается с целочисленным арифметиком и не считается ошибкой. Однако это может привести к образованию кода исключения. Одним из примеров является исключением «арифметично: / на нулю», если результат целочисленного понижения используется в качестве дивизора в выражении.

Рассмотрим следующий код:

int bigValue = Integer.MAX_VALUE;
int x = bigValue * 2 / 5;
int y = bigValue / x;

Что приводит к присваиванию X, и последующая оценка BigValue / X бросает исключение, «арифметичноэкцепция: / на ноль» (т. Е. Разделить на ноль), а не у назначения значения 2.

Ожидаемый результат для X будет 858 993,458, что меньше, чем максимальное значение Int 2,147,483,647. Тем не менее, промежуточный результат от оценки integerger.max_value * 2, составит 4 294 967 294, что превышает максимальное значение int и представляет собой -2 в соответствии с 2s комплемента целочисленных представлений. Последующая оценка -2 / 5 оценивает до 0, которая назначается X.

Переставляя выражение для вычисления X к выражению, которое при оценке, делится перед умножением, следующий код:

int bigValue = Integer.MAX_VALUE;
int x = bigValue / 5 * 2;
int y = bigValue / x;

Результаты в X назначаются 858 993 458 и Y, назначаемые 2, которые ожидаются.

Промежуточный результат от BigValue / 5 составляет 429 496,729, что не превышает максимальное значение для INT. Последующая оценка 429 496,729 * 2 не превышает максимальное значение для INT и ожидаемый результат присвоен X. Оценка для Y затем не разделяет на ноль. Оценки для X и Y работают как ожидалось.

Целочисленные значения Java хранятся как и ведут себя в соответствии с 2S дополнением подписали целые представления. Когда результирующее значение будет больше или меньше максимальных или минимальных целочисленных целочисленных целочисленных целочисленных результатов, использующих результаты целочисленного значения 2. В ситуациях, не предназначенных для использования поведения 2S дополнения, которое является наиболее обычными целочисленными арифметическими ситуациями, полученное значение дополнения 2S приведет к тому, что логика программирования или вычислительная ошибка, как показано в примере выше. Отличная статья Википедии описывает 2S комплимент двоичных целых чисел здесь: Два комплемента - Википедия

Есть методы, чтобы избежать непреднамеренного целочисленного переполнения. Течинки могут быть классифицированы как с использованием тестирования предварительного условия, эксплуатации и BigInteger.

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

Универсание содержит с использованием большего примитивного типа для выполнения арифметической операции или эксплуатации, а затем определение, если результирующее значение находится за пределами максимальных или минимальных значений для целого числа. Даже при эксплуатации все еще возможно, что значение или некоторое промежуточное значение в операции или эксплуатации будут за пределами максимальных или минимальных значений для типа UPCAS и привести к переполнению, что также не будет обнаружено и приведет к неожиданным и нежелательным результатам. Благодаря анализу или предварительным условиям может быть возможно предотвратить переполнение с утешением, когда предотвращение без ускорения невозможно или практично. Если задача целых чисел уже длинные примитивные типы, то эксплуатация невозможна с примитивными типами в Java.

Техника BigInteGer содержит использование Biginteger для арифметической операции или выражения с использованием библиотечных методов, использующих BIGINTEGER. Biginteger не переполняется. Это будет использовать всю доступную память, при необходимости. Его арифметические методы обычно имеют значение только немного менее эффективны, чем целочисленные операции. Все еще возможно, что результат, использующий Biginteger, может быть за пределами максимальных или минимальных значений для целого числа, однако, переполнение не будет происходить в арифметике, ведущем к результату. Программирование и дизайн все равно должны будут определять, что делать, если результат Biginteger находится за пределами максимальных или минимальных значений для желаемого примитивного типа результата, например, INT или длинного.

Программа CARNEGIE MELLON Software Engineering Inference Inference Inference Inference INCUSTING INCUSTION создала набор стандартов для безопасного программирования Java. В стандартах, включенных в стандарты, являются методиками предотвращения и обнаружения целочисленного переполнения. Стандарт публикуется как свободно доступный онлайн-ресурс здесь: Sert Oracle Secure Coding Standard для Java

Раздел Стандарта, который описывает и содержит практические примеры методов кодирования для предотвращения или обнаружения целочисленного переполнения, здесь: Num00-j. Обнаружить или предотвратить целочисленное переполнение

Форма книги и форма PDF из стандарта CERT Oracle Secure Coding для Java также доступны.

По умолчанию INT Java и длинная математика молча обернутся на переполнении и подведении. (Целые операции по другим целочисленным типам выполняются, сначала продвигая операнды для INT или длинных, за JLS 4.2.2.)

Как в Java 8, java.lang.Math предоставляет addExact, subtractExact, multiplyExact, incrementExact, decrementExact а также negateExact Статические методы для int, так и для длительных аргументов, которые выполняют названную операцию, бросая арифметичноэкцепцию на переполнении. (Нет метода DeliadeXact - вам придется проверить один особенный случай (MIN_VALUE / -1) сами.)

Как java 8, java.lang.math также предоставляет toIntExact Чтобы отбрасывать договориться до int, бросая арифметицисцепцию, если значение Long не вписывается в Int. Это может быть полезно, например, вычисление суммы INTS, используя неконкуренную длинную математику, затем используя toIntExact отбрасывать в конце в конце (но будьте осторожны, чтобы не дать вашей сумме переполнить).

Если вы все еще используете старую версию Java, Guage Guava предоставляет INTMATH и LOVEMATH Статические методы для проверки добавления, вычитания, умножения и экспоненции (бросание на переполнении). Эти классы также предоставляют методы для вычисления факторов и биномиальных коэффициентов, которые возвращаются MAX_VALUE на переполнении (который менее удобно проверить). Примитивные классы Guava, SignedBytes, UnsignedBytes, Shorts а также Ints, предоставлять checkedCast Методы сужения больших типов (бросание нелегалярГументированногоException на следующем / переполнении, нет Арифметицеxception), а также saturatingCast Методы, которые возвращают MIN_VALUE или MAX_VALUE на переполнении.

Просто вроде бегите в эту проблему сам, вот мое решение (для умножения и добавления):

static boolean wouldOverflowOccurwhenMultiplying(int a, int b) {
    // If either a or b are Integer.MIN_VALUE, then multiplying by anything other than 0 or 1 will result in overflow
    if (a == 0 || b == 0) {
        return false;
    } else if (a > 0 && b > 0) { // both positive, non zero
        return a > Integer.MAX_VALUE / b;
    } else if (b < 0 && a < 0) { // both negative, non zero
        return a < Integer.MAX_VALUE / b;
    } else { // exactly one of a,b is negative and one is positive, neither are zero
        if (b > 0) { // this last if statements protects against Integer.MIN_VALUE / -1, which in itself causes overflow.
            return a < Integer.MIN_VALUE / b;
        } else { // a > 0
            return b < Integer.MIN_VALUE / a;
        }
    }
}

boolean wouldOverflowOccurWhenAdding(int a, int b) {
    if (a > 0 && b > 0) {
        return a > Integer.MAX_VALUE - b;
    } else if (a < 0 && b < 0) {
        return a < Integer.MIN_VALUE - b;
    }
    return false;
}

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

Существуют библиотеки, которые обеспечивают безопасные арифметические операции, которые проверяют целочисленное переполнение / inlovflow. Например, гуава Intmath.Checkedadd (int a, int b) возвращает сумму a а также b, при условии, что он не переполняется и бросает ArithmeticException если a + b переполняет в подписанном виде int арифметика.

Это обертывается.

например:

public class Test {

    public static void main(String[] args) {
        int i = Integer.MAX_VALUE;
        int j = Integer.MIN_VALUE;

        System.out.println(i+1);
        System.out.println(j-1);
    }
}

принты

-2147483648
2147483647

Я думаю, что вы должны использовать что-то вроде этого, и это называется Upcasting:

public int multiplyBy2(int x) throws ArithmeticException {
    long result = 2 * (long) x;    
    if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE){
        throw new ArithmeticException("Integer overflow");
    }
    return (int) result;
}

Вы можете читать дальше здесь:Обнаружить или предотвратить целочисленное переполнение

Это довольно надежный источник.

Это ничего не делает - под / переполнение просто происходит.

A «-1», который является результатом вычисления, которые переполнены, не отличаются от «-1», который привел к любой другой информации. Таким образом, вы не можете сказать через некоторой статус или осматривать только значение, будь то переполнен.

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

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

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

static final int safeMultiply(int left, int right)
                 throws ArithmeticException {
  if (right > 0 ? left > Integer.MAX_VALUE/right
                  || left < Integer.MIN_VALUE/right
                : (right < -1 ? left > Integer.MIN_VALUE/right
                                || left < Integer.MAX_VALUE/right
                              : right == -1
                                && left == Integer.MIN_VALUE) ) {
    throw new ArithmeticException("Integer overflow");
  }
  return left * right;
}

static final int safeDivide(int left, int right)
                 throws ArithmeticException {
  if ((left == Integer.MIN_VALUE) && (right == -1)) {
    throw new ArithmeticException("Integer overflow");
  }
  return left / right;
}

static final int safeNegate(int a) throws ArithmeticException {
  if (a == Integer.MIN_VALUE) {
    throw new ArithmeticException("Integer overflow");
  }
  return -a;
}
static final int safeAbs(int a) throws ArithmeticException {
  if (a == Integer.MIN_VALUE) {
    throw new ArithmeticException("Integer overflow");
  }
  return Math.abs(a);
}

Я думаю, что это должно быть в порядке.

static boolean addWillOverFlow(int a, int b) {
    return (Integer.signum(a) == Integer.signum(b)) && 
            (Integer.signum(a) != Integer.signum(a+b)); 
}

Есть один случай, который не упоминается выше:

int res = 1;
while (res != 0) {
    res *= 2;

}
System.out.println(res);

будет производить:

0

Этот случай обсуждался здесь:Целочисленный переполнение производит ноль.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top