¿Cómo puedo comprobar si la multiplicación de dos números en Java provocará un desbordamiento?
-
11-09-2019 - |
Pregunta
Quiero manejar el caso especial en el que la multiplicación de dos números juntos provoca un desbordamiento. El código es como la siguiente:
int a = 20;
long b = 30;
// if a or b are big enough, this result will silently overflow
long c = a * b;
Esto es una versión simplificada. En el programa de a
real y b
tienen su origen 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
son siempre no negativo en mi escenario
Solución
Java 8 tiene Math.multiplyExact
, etc., para Math.addExact
enteros y larga. Estos lanzan una ArithmeticException
sin control en caso de desbordamiento.
Otros consejos
Si a
y b
son positivos entonces se puede usar:
if (a != 0 && b > Long.MAX_VALUE / a) {
// Overflow
}
Si usted necesita para hacer frente a los dos 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 Azoté hasta comprobar 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
Hay bibliotecas de Java que proporcionan operaciones aritméticas seguras, que comprueban largo de desbordamiento / subdesbordamiento. Por ejemplo, de Guava LongMath.checkedMultiply (largo a, larga b) devuelve el producto de a
y b
, siempre que no se desborde, y lanza ArithmeticException
si los desbordamientos a * b
en aritmética long
firmado.
Se puede usar java.math.BigInteger lugar y comprobar el tamaño del resultado (no han 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()
}
Use logaritmos para comprobar el tamaño del resultado.
¿Tiene Java tiene algo así como int.MaxValue? Si es así, intente
if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
// it will overflow
}
editar: Long.MAX_VALUE visto en cuestión
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 va a tomar un poco de desbordamientos y sin ninguna división.
Esta es la manera más simple 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"
}
No estoy seguro de por qué nadie está mirando solución como:
if (Long.MAX_VALUE/a > b) {
// overflows
}
Elija un ser mayor de los dos números.
Me gustaría construir sobre la respuesta de Juan Kugelman sin reemplazarlo editando directamente. Se trabaja para su caso de prueba (MIN_VALUE = -10
, MAX_VALUE = 10
) debido a la simetría de MIN_VALUE == -MAX_VALUE
, lo cual no es el caso de dos números enteros de complemento. En la actualidad, 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 a la verdadera MIN_VALUE
y MAX_VALUE
, la respuesta de Juan Kugelman produce un caso de desbordamiento cuando a == -1
y b ==
cualquier otra cosa (primer punto planteado por Kyle). He aquí una forma 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 cualquier MIN_VALUE
y MAX_VALUE
, pero es general para Long
y Integer
de Java y cualquier valor de a
y b
.
Tal vez:
if(b!= 0 && a * b / b != a) //overflow
No está seguro acerca de esta "solución".
Editar:!. Añadido b = 0
Antes de downvote : no se optimizará a * b / b. Esto sería error del compilador. Todavía no veo un caso en que el error de desbordamiento puede ser enmascarada.
Tal vez esto le ayudará a:
/**
* @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;
}
}
Como se ha señalado, Java 8 tiene métodos Math.xxxExact que generen excepciones en caso de desbordamiento.
Si no está utilizando Java 8 para su proyecto, todavía se puede "prestar" sus implementaciones que son bastante compacto.
Aquí hay algunos enlaces a estas implementaciones en un sitio web tercera parte, no hay garantía si éstos permanecen válidos, pero en cualquier caso, usted debe ser capaz de entrar en la fuente del 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, etc.
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 encuentro 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 letra grande (int * int poner el resultado a largo, largo tiempo * poner a Int64)
resultado 2.cmp >> bits y resultado >> (bits - 1)