¿Cómo puedo comprobar si la multiplicación de dos números en Java provocará un desbordamiento?

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

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

.
¿Fue útil?

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)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top