variable tipos tipo entero ejemplos declaracion datos java integer overflow robustness correctness

java - tipos - variable int



¿Cómo maneja Java los flujos y desbordamientos enteros y cómo lo verificaría? (12)

¿Cómo maneja Java los flujos bajos y desbordamientos enteros?

A partir de eso, ¿cómo verificaría / probaría que esto está ocurriendo?


Bueno, en lo que respecta a los tipos enteros primitivos, Java no maneja Over / Underflow en absoluto (para float y double el comportamiento es diferente, se nivelará a +/- infinito tal como lo exige IEEE-754).

Al agregar dos int, no obtendrá ninguna indicación cuando ocurra un desbordamiento. Un método simple para verificar el desbordamiento es usar el siguiente tipo más grande para realizar la operación y verificar si el resultado aún está dentro del rango para el tipo de fuente:

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; }

Lo que harías en lugar de las cláusulas throw, depende de los requisitos de tu aplicación (throw, flush a min / max o simplemente registra lo que sea). Si desea detectar desbordamiento en operaciones largas, no tiene suerte con primitivos, use BigInteger en su lugar.

Editar (2014-05-21): Dado que esta pregunta parece referirse con bastante frecuencia y tuve que resolver el mismo problema yo mismo, es bastante fácil evaluar la condición de desbordamiento por el mismo método que una CPU calcularía su bandera V.

Básicamente es una expresión booleana que implica el signo de ambos operandos y el resultado:

/** * 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; }

En java, es más fácil aplicar la expresión (en el if) a los 32 bits completos, y verificar el resultado usando <0 (esto probará efectivamente el bit de signo). El principio funciona exactamente igual para todos los tipos primitivos enteros , cambiando todas las declaraciones en el método anterior por mucho tiempo hace que funcione por mucho tiempo.

Para tipos más pequeños, debido a la conversión implícita a int (ver JLS para operaciones en modo bit para más detalles), en lugar de verificar <0, la verificación necesita enmascarar el bit de signo explícitamente (0x8000 para operandos cortos, 0x80 para operandos de bytes, ajustar moldes y declaración de parámetro apropiadamente):

/** * 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; }

(Tenga en cuenta que el ejemplo anterior usa la expresión necesidad de restar la detección de desbordamiento)

Entonces, ¿cómo / por qué funcionan estas expresiones booleanas? En primer lugar, algunos pensamientos lógicos revelan que un desbordamiento solo puede ocurrir si los signos de ambos argumentos son los mismos. Porque, si un argumento es negativo y uno positivo, el resultado (de agregar) debe estar más cerca de cero, o en el caso extremo un argumento es cero, igual que el otro argumento. Dado que los argumentos por sí mismos no pueden crear una condición de desbordamiento, su suma tampoco puede crear un desbordamiento.

Entonces, ¿qué sucede si ambos argumentos tienen el mismo signo? Echemos un vistazo al caso, ambos son positivos: al agregar dos argumentos que crean una suma mayor que los tipos MAX_VALUE, siempre se obtendrá un valor negativo, por lo que se produce un desbordamiento si arg1 + arg2> MAX_VALUE. Ahora el valor máximo que podría resultar sería MAX_VALUE + MAX_VALUE (el caso extremo ambos argumentos son MAX_VALUE). Para un byte (ejemplo) que significaría 127 + 127 = 254. Al observar las representaciones de bits de todos los valores que pueden resultar de agregar dos valores positivos, se encuentra que aquellos que se desbordan (de 128 a 254) todos tienen el bit 7 establecido, mientras que todo lo que no se desborda (0 a 127) tiene el bit 7 (arriba, signo) borrado. Eso es exactamente lo que verifica la primera parte (derecha) de la expresión:

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

(~ s & ~ d & r) se convierte en verdadero, solo si , ambos operandos (s, d) son positivos y el resultado (r) es negativo (la expresión funciona en los 32 bits, pero el único bit que nos interesa es el bit más alto (signo), que se compara con <0).

Ahora bien, si ambos argumentos son negativos, su suma nunca puede estar más cerca de cero que cualquiera de los argumentos, la suma debe estar más cerca de menos infinito. El valor más extremo que podemos producir es MIN_VALUE + MIN_VALUE, que (de nuevo para el ejemplo de byte) muestra que para cualquier valor dentro del rango (-1 a -128) se establece el bit de signo, mientras que cualquier posible valor de desbordamiento (-129 a -256 ) tiene el bit de signo despejado. Entonces, el signo del resultado revela nuevamente la condición de desbordamiento. Eso es lo que la mitad izquierda (s & d & ~ r) verifica para el caso donde ambos argumentos (s, d) son negativos y un resultado positivo. La lógica es en gran medida equivalente al caso positivo; todos los patrones de bit que pueden resultar de agregar dos valores negativos tendrán el bit de signo borrado si y solo si ocurre un flujo inferior.


Creo que deberías usar algo como esto y se llama 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; }

Puede leer más aquí: Detectar o prevenir el desbordamiento de enteros

Es una fuente bastante confiable.


Creo que esto debería estar bien.

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


Después de haberme encontrado con este problema, esta es mi solución (tanto para la multiplicación como para la adición):

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; }

no dude en corregir si está mal o si se puede simplificar. He hecho algunas pruebas con el método de multiplicación, en su mayoría casos extremos, pero aún podría estar mal.


Hay bibliotecas que proporcionan operaciones aritméticas seguras, que comprueban el desbordamiento / subdesbordamiento de enteros. Por ejemplo, Guava''s IntMath.checkedAdd (int a, int b) devuelve la suma de a y b , siempre que no se desborde, y arroja ArithmeticException si a + b desborda en aritmética int firmada.


Hay un caso, que no se menciona arriba:

int res = 1; while (res != 0) { res *= 2; } System.out.println(res);

Producirá:

0

Este caso fue discutido aquí: El desbordamiento de enteros produce cero.


Java no hace nada con el desbordamiento de enteros para los tipos de primitiva larga o larga e ignora el desbordamiento con enteros positivos y negativos.

Esta respuesta describe primero el desbordamiento de enteros, brinda un ejemplo de cómo puede suceder, incluso con valores intermedios en la evaluación de expresiones, y luego proporciona enlaces a recursos que brindan técnicas detalladas para prevenir y detectar el desbordamiento de enteros.

La aritmética de enteros y las expresiones que resuenan en desbordamientos inesperados o no detectados son un error de programación común. El desbordamiento de entero inesperado o no detectado es también un problema de seguridad explotable bien conocido, especialmente porque afecta a los objetos de matriz, pila y lista.

El desbordamiento puede ocurrir en una dirección positiva o negativa donde el valor positivo o negativo estaría más allá de los valores máximos o mínimos para el tipo primitivo en cuestión. El desbordamiento puede ocurrir en un valor intermedio durante la evaluación de expresión o operación y afectar el resultado de una expresión u operación en la que se espera que el valor final esté dentro del rango.

A veces, el desbordamiento negativo se denomina erróneamente desbordamiento. Subdesbordamiento es lo que sucede cuando un valor sería más cercano a cero de lo que permite la representación. Underflow ocurre en aritmética entera y es esperado. El subdesbordamiento del entero ocurre cuando una evaluación entera estaría entre -1 y 0 o 0 y 1. Lo que sería un resultado fraccionario se trunca a 0. Esto es normal y esperado con aritmética entera y no se considera un error. Sin embargo, puede provocar que el código arroje una excepción. Un ejemplo es una excepción "ArithmeticException: / by zero" si el resultado de un subdesbordamiento entero se usa como un divisor en una expresión.

Considera el siguiente código:

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

que da como resultado que x se asigne 0 y la evaluación posterior de bigValue / x arroja una excepción, "ArithmeticException: / by zero" (es decir, divide por cero), en lugar de que a y se le asigne el valor 2.

El resultado esperado para x sería 858,993,458 que es menor que el valor int máximo de 2,147,483,647. Sin embargo, el resultado intermedio de la evaluación de Integer.MAX_Value * 2 sería 4.294.967.294, que excede el valor int máximo y es -2 según las representaciones de entero complementarias de 2s. La evaluación posterior de -2 / 5 se evalúa a 0, que se asigna a x.

Reordenar la expresión para calcular x a una expresión que, cuando se evalúa, divide antes de multiplicar, el siguiente código:

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

los resultados en x se asignan 858,993,458 y y se le asigna 2, lo cual se espera.

El resultado intermedio de bigValue / 5 es 429,496,729 que no supera el valor máximo para un int. La evaluación posterior de 429,496,729 * 2 no excede el valor máximo para un int y el resultado esperado se asigna a x. La evaluación para y no se divide por cero. Las evaluaciones para xey funcionan como se espera.

Los valores enteros de Java se almacenan y se comportan de acuerdo con las representaciones de entero con signo del complemento 2s. Cuando un valor resultante sea mayor o menor que los valores enteros máximo o mínimo, en su lugar se obtiene un valor entero de complemento de 2. En situaciones no diseñadas expresamente para usar el comportamiento complementario 2s, que es la mayoría de las situaciones aritméticas enteras ordinarias, el valor complementario 2s resultante causará una lógica de programación o un error de cálculo como se mostró en el ejemplo anterior. Un excelente artículo de Wikipedia describe 2s enteros binarios de cumplidos aquí: complemento de dos - Wikipedia

Existen técnicas para evitar el desbordamiento de enteros involuntarios. Los Techinques pueden clasificarse como pruebas de precondición, upcasting y BigInteger.

La prueba de condición previa consiste en examinar los valores que entran en una operación o expresión aritmética para garantizar que no ocurra un desbordamiento con esos valores. La programación y el diseño necesitarán crear pruebas que aseguren que los valores de entrada no causarán un desbordamiento y luego determinar qué hacer si ocurren valores de entrada que causarán un desbordamiento.

Upcasting comprende el uso de un tipo primitivo más grande para realizar la operación o expresión aritmética y luego determinar si el valor resultante está más allá de los valores máximos o mínimos para un entero. Incluso con upcasting, aún es posible que el valor o algún valor intermedio en una operación o expresión supere los valores máximos o mínimos para el tipo upcast y cause un desbordamiento, que tampoco se detectará y provocará resultados inesperados y no deseados. A través de análisis o condiciones previas, es posible evitar el desbordamiento de la transmisión ascendente cuando la prevención sin retransmisión no es posible o práctica. Si los números enteros en cuestión ya son tipos primitivos largos, la subida no es posible con tipos primitivos en Java.

La técnica de BigInteger comprende el uso de BigInteger para la operación o expresión aritmética utilizando los métodos de biblioteca que usan BigInteger. BigInteger no se desborda. Utilizará toda la memoria disponible, si es necesario. Sus métodos aritméticos son normalmente ligeramente menos eficientes que las operaciones enteras. Todavía es posible que un resultado que utiliza BigInteger pueda estar más allá de los valores máximos o mínimos para un entero, sin embargo, no ocurrirá un desbordamiento en la aritmética que conduce al resultado. La programación y el diseño aún deberán determinar qué hacer si un resultado de BigInteger va más allá de los valores máximos o mínimos para el tipo de resultado primitivo deseado, por ejemplo, int o largo.

El programa CERT del Instituto de Ingeniería de Software Carnegie Mellon y Oracle han creado un conjunto de estándares para la programación segura de Java. Se incluyen en las normas técnicas para prevenir y detectar el desbordamiento de enteros. El estándar se publica como un recurso en línea de acceso libre aquí: El CERT Oracle Secure Coding Standard para Java

La sección del estándar que describe y contiene ejemplos prácticos de técnicas de codificación para prevenir o detectar el desbordamiento de enteros está aquí: NUM00-J. Detecta o previene el desbordamiento de enteros

El formulario de libro y el formato PDF de CERT Oracle Secure Coding Standard para Java también están disponibles.


No hace nada: el subdesbordamiento / desbordamiento simplemente sucede.

Un "-1" que es el resultado de un cálculo que se desbordó no es diferente del "-1" que resultó de cualquier otra información. Por lo tanto, no puede decir a través de algún estado o inspeccionando solo un valor si está desbordado.

Pero puede ser inteligente con sus cálculos para evitar el desbordamiento, si es que importa, o al menos saber cuándo sucederá. ¿Cuál es tu situación?


Por defecto, las operaciones int y long de Java se ajustan silenciosamente en caso de desbordamiento y subdesbordamiento. (Las operaciones enteras en otros tipos de enteros se realizan promocionando primero los operandos a int o long, según JLS 4.2.2 ).

A partir de Java 8, java.lang.Math proporciona métodos estáticos addExact , subtractExact , multiplyExact , incrementExact , decrementExact y negateExact para los argumentos int y long que realizan la operación nombrada, lanzando ArithmeticException en overflow. (No existe el método de divideExact -usted tendrá que verificar el caso especial ( MIN_VALUE / -1 ) usted mismo).

A partir de Java 8, java.lang.Math también proporciona toIntExact para lanzar un long a un int, lanzando ArithmeticException si el valor de long no cabe en un int. Esto puede ser útil, por ejemplo, para calcular la suma de los enteros utilizando matemáticas largas no comprobadas, y luego usar toIntExact para convertir a int al final (pero tenga cuidado de no dejar que su suma se desborde).

Si todavía está utilizando una versión anterior de Java, Google Guava proporciona los métodos estáticos de IntMath y LongMath para comprobar suma, resta, multiplicación y exponenciación (lanzar en desbordamiento). Estas clases también proporcionan métodos para calcular factoriales y coeficientes binomiales que devuelven MAX_VALUE en desbordamiento (lo cual es menos conveniente verificar). Las clases de utilidad primitivas de Guava, SignedBytes , UnsignedBytes , Shorts y Ints , proporcionan métodos checkedCast para restringir tipos más grandes (lanzando IllegalArgumentException en under / overflow, no ArithmeticException), así como métodos saturatingCast que devuelven MIN_VALUE o MAX_VALUE en overflow.


Se envuelve.

p.ej:

prueba de clase pública {

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); }

}

huellas dactilares

-2147483648

2147483647


Si se desborda, vuelve al valor mínimo y continúa desde allí. Si se desborda, vuelve al valor máximo y continúa desde allí.

Puede verificarlo de antemano de la siguiente manera:

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; } }

(puede sustituir int por long para realizar los mismos controles por long )

Si piensa que esto puede ocurrir más que a menudo, considere usar un tipo de datos u objeto que pueda almacenar valores más grandes, por ejemplo, long o quizás java.math.BigInteger . El último no se desborda, prácticamente, la memoria JVM disponible es el límite.

Si ya estás en Java8, puedes utilizar los nuevos Math#addExact() y Math#subtractExact() que arrojarán una ArithmeticException en overflow.

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; } }

El código fuente se puede encontrar here y here respectivamente.

Por supuesto, también puede usarlos de inmediato en lugar de ocultarlos en un método de utilidad boolean .


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); }