¿Cómo maneja Java los desbordamientos y desbordamientos de enteros?
A partir de eso, ¿cómo verificaría / probaría que esto está ocurriendo?
java
integer
integer-overflow
KushalP
fuente
fuente
checked
hasta donde yo sé. No veo que se use mucho, y escribirchecked { code; }
es casi tanto trabajo como llamar a un método.csc /checked ...
o establezca la propiedad en el panel de propiedades del proyecto en Visual Studio.Respuestas:
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 verificar eso de antemano de la siguiente manera:
(puede sustituir
int
porlong
realizar las mismas comprobacioneslong
)Si cree que esto puede ocurrir con más frecuencia, considere usar un tipo de datos u objeto que pueda almacenar valores más grandes, por ejemplo,
long
o tal vezjava.math.BigInteger
. El último no se desborda, prácticamente, la memoria JVM disponible es el límite.Si ya está en Java8, puede hacer uso de los nuevos
Math#addExact()
yMath#subtractExact()
métodos que generarán unArithmeticException
desbordamiento.El código fuente se puede encontrar aquí y aquí. respectivamente.
Por supuesto, también puede usarlos de inmediato en lugar de ocultarlos en un
boolean
método de utilidad.fuente
+100, -100
, respectivamente. Si estuviera agregando uno a un entero Java, el proceso se vería así cuando se desbordara.98, 99, 100, -100, -99, -98, ...
. ¿Eso tiene más sentido?Math#addExact
es la sintaxis que normalmente se usa al escribir javadocs, aunque normalmente se convertiríaMath.addExact
, a veces la otra forma simplemente seIf it underflows, it goes back to the maximum value and continues from there.
- parece haber confundido el desbordamiento con el desbordamiento negativo. El desbordamiento en enteros ocurre todo el tiempo (cuando el resultado es una fracción).Bueno, en lo que respecta a los tipos enteros primitivos, Java no maneja Over / Underflow en absoluto (para flotante y el doble del comportamiento es diferente, se vaciará 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 realmente realizar la operación y verificar si el resultado todavía está dentro del rango para el tipo de fuente:
Lo que haría en lugar de las cláusulas de lanzamiento depende de los requisitos de sus aplicaciones (lanzar, vaciar a min / max o simplemente registrar lo que sea). Si desea detectar el desbordamiento en operaciones largas, no tiene suerte con las primitivas, use BigInteger en su lugar.
Editar (2014-05-21): Dado que esta pregunta parece ser mencionada 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 indicador V.
Básicamente es una expresión booleana que involucra el signo de ambos operandos, así como el resultado:
En java es más simple 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 a largo lo hace funcionar por mucho tiempo.
Para tipos más pequeños, debido a la conversión implícita a int (vea el JLS para operaciones bit a 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 conversiones) y declaración de parámetros adecuadamente):
(Tenga en cuenta que el ejemplo anterior usa la expresión necesidad de detección de desbordamiento de resta )
Entonces, ¿cómo / por qué funcionan estas expresiones booleanas? Primero, un pensamiento lógico revela 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 sumar) 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: agregar dos argumentos que crean una suma mayor que los tipos MAX_VALUE, siempre generará 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 (en el caso extremo, ambos argumentos son MAX_VALUE). Para un byte (ejemplo) que significaría 127 + 127 = 254. Mirando las representaciones de bits de todos los valores que pueden resultar de agregar dos valores positivos, uno encuentra que aquellos que se desbordan (128 a 254) tienen el bit 7 establecido, mientras que todos los que no se desborden (0 a 127) tienen el bit 7 (superior, signo) borrado. Eso es exactamente lo que comprueba la primera parte (derecha) de la expresión:
(~ s & ~ d & r) se vuelve 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, 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 (nuevamente por ejemplo de byte) muestra que para cualquier valor dentro del rango (-1 a -128) se establece el bit de signo, mientras que cualquier valor de desbordamiento posible (-129 a -256 ) tiene el bit de signo borrado. Entonces, el signo del resultado nuevamente revela la condición de desbordamiento. Eso es lo que la mitad izquierda (s & d & ~ r) verifica para el caso en que ambos argumentos (s, d) son negativos y un resultado que es positivo. La lógica es en gran medida equivalente al caso positivo; todos los patrones de bits que pueden resultar de agregar dos valores negativos tendrán el bit de signo borrado si y solo si ocurrió un flujo inferior.
fuente
Por defecto, las matemáticas int y largas de Java se ajustan silenciosamente al desbordamiento y al desbordamiento. (Las operaciones de enteros en otros tipos de enteros se realizan promoviendo primero los operandos a int o long, según JLS 4.2.2 .)
A partir de Java 8,
java.lang.Math
proporcionaaddExact
,subtractExact
,multiplyExact
,incrementExact
,decrementExact
ynegateExact
los métodos estáticos, tanto para int y largas discusiones que llevan a cabo la operación de llamada, lanzando ArithmeticException en caso de desbordamiento. (No existe un método divideExact: deberá verificar el caso especial (MIN_VALUE / -1
) usted mismo).A partir de Java 8, java.lang.Math también proporciona
toIntExact
convertir un largo a un int, lanzando ArithmeticException si el valor del largo no cabe en un int. Esto puede ser útil para, por ejemplo, calcular la suma de entradas usando cálculos matemáticos largos no verificados, luego usartoIntExact
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 métodos estáticos IntMath y LongMath para sumar, restar, multiplicar y exponenciar marcados (lanzar desbordamientos). Estas clases también proporcionan métodos para calcular factores factoriales y coeficientes binomiales que retornan
MAX_VALUE
en el desbordamiento (que es menos conveniente verificar). Clases de utilidad primitivas de guayaba,SignedBytes
,UnsignedBytes
,Shorts
yInts
, proporcionancheckedCast
métodos para la reducción de los tipos más grandes (que lanzan IllegalArgumentException en bajo / desbordamiento, no ArithmeticException), así comosaturatingCast
métodos que devuelvenMIN_VALUE
oMAX_VALUE
en desbordamiento.fuente
Java no hace nada con desbordamiento de enteros para los tipos primitivos int o long e ignora el desbordamiento con enteros positivos y negativos.
Esta respuesta describe primero el desbordamiento de enteros, da un ejemplo de cómo puede suceder, incluso con valores intermedios en la evaluación de expresiones, y luego proporciona enlaces a recursos que proporcionan técnicas detalladas para prevenir y detectar el desbordamiento de enteros.
La aritmética entera y las expresiones que resultan en un desbordamiento inesperado o no detectado son un error de programación común. El desbordamiento de enteros inesperado o no detectado también es 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 la expresión u operación y afectar el resultado de una expresión u operación donde se esperaría que el valor final esté dentro del rango.
A veces, el desbordamiento negativo se denomina erróneamente desbordamiento. Underflow es lo que sucede cuando un valor estaría más cerca de cero de lo que permite la representación. El subflujo se produce en aritmética de enteros y se espera. El desbordamiento de enteros ocurre cuando una evaluación de enteros 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 de enteros 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 del desbordamiento de enteros se usa como divisor en una expresión.
Considere el siguiente código:
lo que da como resultado que a x se le asigne 0 y la evaluación posterior de bigValue / x arroja una excepción, "ArithmeticException: / by zero" (es decir, dividir por cero), en lugar de y se le asigna 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 evaluar Integer.MAX_Value * 2, sería 4,294,967,294, que excede el valor int máximo y es -2 de acuerdo con las representaciones enteras del complemento 2s. La evaluación posterior de -2 / 5 evalúa a 0, que se asigna a x.
Reorganizando la expresión para calcular x a una expresión que, cuando se evalúa, divide antes de multiplicar el siguiente código:
da como resultado que x se asigne 858,993,458 e y se asigne 2, lo cual se espera.
El resultado intermedio de bigValue / 5 es 429,496,729 que no excede 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 luego no se divide por cero. Las evaluaciones para x e y funcionan como se esperaba.
Los valores enteros de Java se almacenan como y se comportan de acuerdo con las representaciones enteras con signo del complemento 2s. Cuando un valor resultante sería mayor o menor que los valores enteros máximos o mínimos, en su lugar se obtiene un valor entero complementario de 2. En situaciones no diseñadas expresamente para utilizar el comportamiento del complemento 2s, que es la aritmética de enteros más común, el valor del complemento 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 los enteros binarios complementarios 2s aquí: complemento de dos - Wikipedia
Existen técnicas para evitar el desbordamiento entero involuntario. Las Techinques pueden clasificarse según el uso de pruebas de condición previa, transmisión y BigInteger.
La prueba previa a la condición comprende examinar los valores que entran en una operación o expresión aritmética para garantizar que no se produzca un desbordamiento con esos valores. La programación y el diseño deberán crear pruebas que garanticen que los valores de entrada no causen desbordamiento y luego determinar qué hacer si se producen valores de entrada que causen desbordamiento.
La conversión ascendente 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 número entero. Incluso con la conversión ascendente, 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 de conversión ascendente y provoque un desbordamiento, que tampoco se detectará y causará resultados inesperados y no deseados. A través del análisis o las condiciones previas, puede ser posible evitar el desbordamiento con la transmisión cuando la prevención sin transmisión no es posible o práctica. Si los enteros en cuestión ya son tipos primitivos largos, entonces la conversión ascendente no es posible con tipos primitivos en Java.
La técnica BigInteger comprende el uso de BigInteger para la operación o expresión aritmética utilizando 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 solo un poco menos eficientes que las operaciones de enteros. Todavía es posible que un resultado usando BigInteger pueda estar más allá de los valores máximos o mínimos para un entero, sin embargo, no se producirá 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 está más allá de los valores máximos o mínimos para el tipo de resultado primitivo deseado, por ejemplo, int o long.
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. Las normas incluyen técnicas para prevenir y detectar el desbordamiento de enteros. El estándar se publica como un recurso en línea de libre acceso aquí: El estándar de codificación segura Oracle CERT 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. Detectar o prevenir el desbordamiento de enteros
También están disponibles el formulario de libro y el formulario PDF de CERT Oracle Secure Coding Standard para Java.
fuente
Habiendo tenido este problema, esta es mi solución (tanto para la multiplicación como para la suma):
siéntase libre de corregir si está mal o si se puede simplificar. He realizado algunas pruebas con el método de multiplicación, en su mayoría casos extremos, pero aún podría estar equivocado.
fuente
int*int
, creo que simplemente echar un vistazolong
y ver si el resultado encajaint
sería el enfoque más rápido. Porquelong*long
, si uno normaliza los operandos para que sean positivos, se puede dividir cada uno en mitades superior e inferior de 32 bits, promover cada mitad a largo (¡tenga cuidado con las extensiones de signos!) Y luego calcular dos productos parciales [una de las mitades superiores debería ser cero].Hay bibliotecas que proporcionan operaciones aritméticas seguras, que verifican el desbordamiento / desbordamiento de enteros. Por ejemplo, IntMath.checkedAdd (int a, int b) de Guava devuelve la suma de
a
yb
, siempre que no se desborde, y arrojaArithmeticException
si sea + b
desborda enint
aritmética con signo .fuente
Math
clase contiene un código similar.Se envuelve.
p.ej:
huellas dactilares
fuente
Creo que deberías usar algo como esto y se llama Upcasting:
Puede leer más aquí: Detecte o evite el desbordamiento de enteros
Es una fuente bastante confiable.
fuente
No hace nada: el desbordamiento / 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 saber a través de algún estado o inspeccionando solo un valor si se desbordó.
Pero puede ser inteligente con sus cálculos para evitar el desbordamiento, si es importante, o al menos saber cuándo sucederá. Cual es tu situacion
fuente
fuente
Creo que esto debería estar bien.
fuente
Hay un caso, que no se menciona anteriormente:
Producirá:
Este caso se discutió aquí: el desbordamiento de enteros produce cero.
fuente