Come posso verificare se moltiplicare due numeri in Java causerà un overflow?

Voglio gestire il caso speciale in cui moltiplicando due numeri insieme provoca un overflow. Il codice ha un aspetto simile al seguente:

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

Questa è una versione semplificata. Nel vero programma b sono reperibili altrove in fase di esecuzione. Quello che voglio ottenere è qualcosa del genere:

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

Come suggerisci che sia il codice migliore?

Aggiornamento: b sono sempre non negativi nel mio scenario.

Java 8 ha Math.multiplyExact , Math.addExact ecc. Per interi e lunghi. Questi lanciano un ArithmeticException non controllato su overflow.

Se a e b sono entrambi positivi, puoi usare:

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

Se hai bisogno di gestire sia numeri positivi che negativi allora è più complicato:

 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 } 

Ecco un piccolo tavolo che ho montato per controllare questo, facendo finta che l'overflow si verifichi 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 

Esistono librerie Java che forniscono operazioni aritmetiche sicure, che controllano l’overflow / underflow lungo. Ad esempio, LongMath.checkedMultiply di Guava (long a, long b) restituisce il prodotto di b , a condizione che non esageri e getta ArithmeticException se a * b fuoriesce in aritmetica long firmata.

È ansible utilizzare java.math.BigInteger e verificare la dimensione del risultato (non aver testato il codice):

 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() } 

Utilizzare i logaritmi per verificare la dimensione del risultato.

Java ha qualcosa come int.MaxValue? Se sì, allora prova

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

modifica: visto Long.MAX_VALUE in questione

Rubato da jruby

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

AGGIORNAMENTO: questo codice è breve e funziona bene; tuttavia, non riesce per a = -1, b = Long.MIN_VALUE.

Un ansible miglioramento:

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

Nota che questo colpirà alcuni overflow senza alcuna divisione.

Non sono sicuro del motivo per cui nessuno sta guardando una soluzione come:

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

Scegli un più grande dei due numeri.

Mi piacerebbe build sulla risposta di John Kugelman senza sostituirlo modificandolo direttamente. Funziona per il suo test case ( MIN_VALUE = -10 , MAX_VALUE = 10 ) a causa della simmetria di MIN_VALUE == -MAX_VALUE , che non è il caso per i numeri interi del complemento a due. In realtà, 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 viene applicato al vero valore MIN_VALUE e MIN_VALUE , la risposta di John Kugelman produce un caso di overflow quando a == -1 b == qualsiasi altra cosa (punto prima sollevato da Kyle). Ecco un modo per risolverlo:

 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 } 

Non è una soluzione generale per MIN_VALUE e MIN_VALUE , ma è generale per Java Long e Integer e qualsiasi valore di a e b .

Può essere:

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

Non sono sicuro di questa “soluzione”.

Modifica: Aggiunto b! = 0.

Prima del downvote : a * b / b non sarà ottimizzato. Questo sarebbe un bug del compilatore. Non vedo ancora un caso in cui il bug di overflow possa essere mascherato.

forse questo ti aiuterà:

 /** * @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; } } 

Come è stato sottolineato, Java 8 ha metodi Math.xxxExact che generano eccezioni su overflow.

Se non stai usando Java 8 per il tuo progetto, puoi ancora “prendere in prestito” le loro implementazioni che sono piuttosto compatte.

Ecco alcuni link a queste implementazioni su un sito web di terze parti, nessuna garanzia che rimarranno validi, ma in ogni caso dovresti essere in grado di andare nella sorgente JDK e vedere come fanno la loro magia all’interno della class 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

ecc. ecc.

Ecco il modo più semplice a cui possa pensare

 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" } 

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, scusa non ho trovato int64 in 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.salva il risultato in caratteri grandi (int * int metti il ​​risultato a long, long * long put a int64)

Risultato 2.cmp >> bit e risultato >> (bit – 1)