valor - tipos de excepciones en java
¿Cómo puedo verificar si multiplicar dos números en Java causará un desbordamiento? (14)
¿Tiene Java algo como int.MaxValue? Si es así, prueba
if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
// it will overflow
}
editar: visto Long.MAX_VALUE en cuestión
Quiero manejar el caso especial donde multiplicar dos números juntos causa un desbordamiento. El código se ve así:
int a = 20;
long b = 30;
// if a or b are big enough, this result will silently overflow
long c = a * b;
Esa es una versión simplificada. En el programa real a
y b
se obtienen en otro lugar en tiempo de ejecución. Lo que quiero lograr es algo como esto:
long c;
if (a * b will overflow) {
c = Long.MAX_VALUE;
} else {
c = a * b;
}
¿Cómo sugieres que mejor código esto?
Actualización: a
y b
siempre son no negativos en mi escenario.
Aquí está la manera más simple en que puedo pensar
int a = 20;
long b = 30;
long c = a * b;
if(c / b == a) {
// Everything fine.....no overflow
} else {
// Overflow case, because in case of overflow "c/b" can''t equal "a"
}
Como se ha señalado, Java 8 tiene métodos Math.xxxExact que arrojan excepciones en overflow.
Si no está utilizando Java 8 para su proyecto, aún puede "tomar prestado" sus implementaciones que son bastante compactas.
Aquí hay algunos enlaces a estas implementaciones en un sitio web de terceros, no hay garantía de si serán válidos pero, en cualquier caso, debería poder acceder a la fuente JDK y ver cómo hacen su magia dentro de la clase 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
etcétera etcétera.
Hay bibliotecas de Java que proporcionan operaciones aritméticas seguras, que controlan el desbordamiento / subdesbordamiento largo. Por ejemplo, LongMath.checkedMultiply de Guava (long a, long b) devuelve el producto de b
, siempre que no se desborde, y arroja ArithmeticException
si a * b
desborda en aritmética long
firmada.
Java 8 tiene Math.multiplyExact
, Math.multiplyExact
, etc. para Math.addExact
y long. Estos lanzan una ArithmeticException
no controlada en overflow.
Me gustaría basarme en la respuesta de John Kugelman sin reemplazarla editándola directamente. Funciona para su caso de prueba ( MIN_VALUE = -10
, MAX_VALUE = 10
) debido a la simetría de MIN_VALUE == -MAX_VALUE
, que no es el caso para los enteros complementarios de dos. En realidad, MIN_VALUE == -MAX_VALUE - 1
.
scala> (java.lang.Integer.MIN_VALUE, java.lang.Integer.MAX_VALUE)
res0: (Int, Int) = (-2147483648,2147483647)
scala> (java.lang.Long.MIN_VALUE, java.lang.Long.MAX_VALUE)
res1: (Long, Long) = (-9223372036854775808,9223372036854775807)
Cuando se aplica al verdadero MIN_VALUE
y MAX_VALUE
, la respuesta de John Kugelman arroja un caso de desbordamiento cuando a == -1
b ==
cualquier otra cosa (punto señalado por primera vez por Kyle). Aquí hay una manera de solucionarlo:
long maximum = Long.signum(a) == Long.signum(b) ? Long.MAX_VALUE : Long.MIN_VALUE;
if ((a == -1 && b == Long.MIN_VALUE) ||
(a != -1 && a != 0 && ((b > 0 && b > maximum / a) ||
(b < 0 && b < maximum / a))))
{
// Overflow
}
No es una solución general para MIN_VALUE
y MAX_VALUE
, pero es general para Long
e Integer
Java y cualquier valor de b
.
No estoy seguro de por qué nadie está buscando soluciones como:
if (Long.MAX_VALUE/a > b) {
// overflows
}
Elija a para ser más grande de los dos números.
Puede usar java.math.BigInteger en su lugar y verificar el tamaño del resultado (no ha probado el código):
BigInteger bigC = BigInteger.valueOf(a) * multiply(BigInteger.valueOf(b));
if(bigC.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
c = Long.MAX_VALUE;
} else {
c = bigC.longValue()
}
Robado de jruby
long result = a * b;
if (a != 0 && result / a != b) {
// overflow
}
ACTUALIZACIÓN: Este código es corto y funciona bien; sin embargo, falla para a = -1, b = Long.MIN_VALUE.
Una posible mejora:
long result = a * b;
if( (Math.signum(a) * Math.signum(b) != Math.signum(result)) ||
(a != 0L && result / a != b)) {
// overflow
}
Tenga en cuenta que esto atrapará algunos desbordamientos sin ninguna división.
Si a
y b
son positivos, puedes usar:
if (a != 0 && b > Long.MAX_VALUE / a) {
// Overflow
}
Si necesita lidiar con números positivos y negativos, entonces es más complicado:
long maximum = Long.signum(a) == Long.signum(b) ? Long.MAX_VALUE : Long.MIN_VALUE;
if (a != 0 && (b > 0 && b > maximum / a ||
b < 0 && b < maximum / a))
{
// Overflow
}
Aquí hay una pequeña mesa que preparé para verificar esto, pretendiendo que el desbordamiento ocurre a -10 o +10:
a = 5 b = 2 2 > 10 / 5
a = 2 b = 5 5 > 10 / 2
a = -5 b = 2 2 > -10 / -5
a = -2 b = 5 5 > -10 / -2
a = 5 b = -2 -2 < -10 / 5
a = 2 b = -5 -5 < -10 / 2
a = -5 b = -2 -2 < 10 / -5
a = -2 b = -5 -5 < 10 / -2
Tal vez:
if(b!= 0 && a * b / b != a) //overflow
No estoy seguro acerca de esta "solución".
Editar: se agregó b! = 0.
Antes de su voto a favor : a * b / b no será optimizado. Esto sería un error del compilador. Todavía no veo un caso en el que se pueda enmascarar el error de desbordamiento.
Use logaritmos para verificar el tamaño del resultado.
c / c ++ (largo * largo):
const int64_ w = (int64_) a * (int64_) b;
if ((long) (w >> sizeof(long) * 8) != (long) w >> (sizeof(long) * 8 - 1))
// overflow
java (int * int, lo siento, no encontré int64 en 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.save el resultado en tipo grande (int * int poner el resultado en long, long * long puesto a int64)
2.cmp resultado >> bits y resultado >> (bits - 1)
tal vez esto te ayude:
/**
* @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;
}
}