Jak sprawdzić, czy mnożenie dwóch liczb w Javie spowoduje przepełnienie?

Chcę zająć się szczególnym przypadkiem, w którym mnożenie dwóch liczb razem powoduje przepełnienie. Kod wygląda mniej więcej tak:

int a = 20;
long b = 30;

// if a or b are big enough, this result will silently overflow
long c = a * b;
To uproszczona wersja. W rzeczywistym Programie a i {[3] } są pozyskiwane gdzie indziej w czasie wykonywania. To co chcę osiągnąć to coś takiego:
long c;
if (a * b will overflow) {
    c = Long.MAX_VALUE;
} else {
    c = a * b;
}
Jak mam to zakodować?

Update: a i b są zawsze nie-negatywne w moim scenariuszu.

Author: Steve McLeod, 2009-11-01

14 answers

Java 8 mA Math.multiplyExact, Math.addExact itd. dla ints i long. Te rzucają niekontrolowane ArithmeticException na przepełnienie.

 75
Author: bcoughlan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-24 12:13:01

Jeśli a i b są dodatnie, możesz użyć:

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

Jeśli musisz poradzić sobie zarówno z liczbami dodatnimi, jak i ujemnymi, to jest to bardziej skomplikowane:

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
}

Oto mały stolik, który podniosłem, aby to sprawdzić, udając, że przepełnienie dzieje się przy -10 lub +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
 55
Author: John Kugelman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-16 15:14:17

Istnieją Biblioteki Javy, które zapewniają bezpieczne operacje arytmetyczne, które sprawdzają długi przepełnienie/niedopełnienie. Na przykład, Guava ' s LongMath.checkedMultiply (long A, long b) Zwraca iloczyn a i b, pod warunkiem, że nie przepełnia się i rzuca ArithmeticException Jeśli a * b przepełnia się w arytmetyce podpisanej long.

 17
Author: reprogrammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-14 11:47:52

Możesz użyć Javy.matematyka.Zamiast tego BigInteger i sprawdź rozmiar wyniku (nie testowałem kodu):

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()
}
 6
Author: Ulf Lindback,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-01 18:19:39

Użyj logarytmów, aby sprawdzić rozmiar wyniku.

 6
Author: High Performance Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-30 02:37:24

Czy Java ma coś takiego jak int.MaxValue? Jeśli tak, spróbuj

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

Edit: seen Long.MAX_VALUE w pytaniu

 4
Author: nothrow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-02 11:38:24

Skradziony z jruby

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

UPDATE: ten kod jest krótki i działa dobrze; jednak zawodzi dla a = -1, B = Long.MIN_VALUE.

Jedno możliwe rozszerzenie:

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

Zauważ, że to wychwyci pewne przepełnienia bez podziału.

 3
Author: rogerdpack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-16 18:37:25

Nie wiem dlaczego nikt nie patrzy na rozwiązanie typu:

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

Wybierz a, aby była większa z dwóch liczb.

 2
Author: fastcodejava,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-30 02:37:32

Chciałbym oprzeć się na odpowiedzi Johna Kugelmana bez zastępowania jej przez edycję bezpośrednio. To działa dla jego przypadku testowego (MIN_VALUE = -10, MAX_VALUE = 10) ze względu na symetrię MIN_VALUE == -MAX_VALUE, co nie ma miejsca w przypadku dwóch liczb całkowitych dopełniających. W rzeczywistości, 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)

Po zastosowaniu do prawdziwych MIN_VALUE i MAX_VALUE, odpowiedź Johna Kugelmana daje przypadek przepełnienia, gdy a == -1 i b == cokolwiek innego (punkt pierwszy podniesiony przez Kyle ' a). Oto jak to naprawić:

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
}

To nie jest ogólne rozwiązanie dla dowolne MIN_VALUE i MAX_VALUE, ale jest ogólne dla Long i Integer oraz dowolnych wartości a i b.

 2
Author: Jim Pivarski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-03 18:26:24

Może:

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

Nie jestem pewien co do tego "rozwiązania".

Edit: Dodano b != 0.

Zanim przegłosujesz: a * b / b nie będzie zoptymalizowane. To byłby błąd kompilatora. Nadal nie widzę przypadku, w którym błąd przepełnienia może być maskowany.

 1
Author: Thomas Jung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-02 08:22:03

Może to ci pomoże:

/**
 * @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;
    }
}
 1
Author: dfa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-02 08:48:57

Jak już wspomniano, Java 8 ma matematykę.metody xxxExact rzucające wyjątki na przepełnienie.

Jeśli nie używasz Javy 8 do swojego projektu, nadal możesz "pożyczyć" ich implementacje, które są dość kompaktowe.

Oto kilka linków do tych implementacji na stronie 3rd party, nie ma gwarancji, czy pozostaną one ważne, ale w każdym razie powinieneś być w stanie wejść do źródła JDK i zobaczyć, jak robią swoją magię wewnątrz java.lang.Math klasy.

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.

 1
Author: StFS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-18 15:14:31

Oto najprostszy sposób, jaki mogę wymyślić

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"
}
 1
Author: Naren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-25 02:33:20

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, sorry I didn ' t find 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.save the result in large type (int*int put the result to long, long*long put to int64)

2.CMP result > > bits and result > >(bits-1)

 -1
Author: xuan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-05 06:18:17