Pergunta

Eu quero lidar com o caso especial em que a multiplicação de dois números juntos faz com que um estouro. A aparência de código algo como isto:

int a = 20;
long b = 30;

// if a or b are big enough, this result will silently overflow
long c = a * b;

Esta é uma versão simplificada. No a programa real e b são originados em outros lugares durante a execução. O que eu quero alcançar é algo como isto:

long c;
if (a * b will overflow) {
    c = Long.MAX_VALUE;
} else {
    c = a * b;
}

Como você sugere que eu melhor código isso?

Update:. a e b são sempre não-negativo no meu cenário

Foi útil?

Solução

Java 8 tem Math.multiplyExact, Math.addExact etc. para ints e longo prazo. Estes lançar uma ArithmeticException desmarcada no estouro.

Outras dicas

Se a e b são ambos positivos, então você pode usar:

if (a != 0 && b > Long.MAX_VALUE / a) {
    // Overflow
}

Se você precisa lidar com ambos os números positivos e negativos, então é mais 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
}

Aqui está uma pequena mesa que eu chicoteado até para verificar isso, fingindo que transbordam acontece a -10 ou +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

Existem bibliotecas Java que fornecem operações aritméticas seguros, que verificam longo estouro / estouro negativo. Por exemplo, de goiaba LongMath.checkedMultiply (por muito tempo um, muito b) retorna o produto de a e b, desde que não transborde, e joga ArithmeticException se a * b transborda em aritmética long assinado.

Você pode usar java.math.BigInteger vez e verifique o tamanho do resultado (não testei o 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 verificar o tamanho do resultado.

O Java tem algo como int.MaxValue? Se sim, então tente

if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
 // it will overflow
}

edit: visto Long.MAX_VALUE em questão

Stolen de jruby

    long result = a * b;
    if (a != 0 && result / a != b) {
       // overflow
    }

UPDATE: Este código é curto e funciona bem; no entanto, ele falha para a = -1, b = Long.MIN_VALUE.

Uma valorização possível:

long result = a * b;
if( (Math.signum(a) * Math.signum(b) != Math.signum(result)) || 
    (a != 0L && result / a != b)) {
    // overflow
}

Note que isso vai pegar alguns excessos sem qualquer divisão.

Aqui é a maneira mais simples que posso 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"
}

Eu não tenho certeza por que ninguém está olhando como solução:

if (Long.MAX_VALUE/a > b) {
     // overflows
} 

Escolha um para ser maior dos dois números.

Eu gostaria de construir sobre a resposta de John Kugelman sem substituí-lo por editá-lo diretamente. Ele trabalha para o seu caso de teste (MIN_VALUE = -10, MAX_VALUE = 10) por causa da simetria de MIN_VALUE == -MAX_VALUE, o que não é o caso de dois de inteiros do complemento. Na realidade, 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)

Quando aplicado ao verdadeiro MIN_VALUE e MAX_VALUE, a resposta de John Kugelman produz um caso de excesso quando a == -1 e b == qualquer outra coisa (primeiro ponto levantado por Kyle). Aqui está uma maneira de corrigi-lo:

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
}

Não é uma solução geral para qualquer MIN_VALUE e MAX_VALUE, mas é geral para Long e Integer de Java e qualquer valor de a e b.

Talvez:

if(b!= 0 && a * b / b != a) //overflow

Não tenho certeza sobre esta "solução".

Edit: b Adicionado = 0

!.

Antes de downvote : a * b / b não será otimizado. Isso seria compilador bug. Eu ainda não vejo um caso em que o bug de estouro pode ser mascarado.

talvez isso irá ajudá-lo:

/**
 * @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 tem sido apontado, Java 8 tem métodos Math.xxxExact que lançam exceções no estouro.

Se você não estiver usando Java 8 para o seu projeto, você ainda pode "emprestar" suas implementações que são bastante compacto.

Aqui estão alguns links para essas implementações em um site 3o partido, nenhuma garantia se estes permanecerá válido, mas em qualquer caso, você deve ser capaz de ir para a fonte JDK e ver como eles fazem sua mágica dentro da classe 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 ++ (long * long):

const int64_ w = (int64_) a * (int64_) b;    
if ((long) (w >> sizeof(long) * 8) != (long) w >> (sizeof(long) * 8 - 1))
    // overflow

java (int * int, desculpe, eu não encontrou int64 em 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 o resultado em grande tipo (int * int colocar o resultado a longo, longo * longa colocada em int64)

resultado 2.cmp >> bits e resultado >> (bits - 1)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top