Java에서 두 숫자를 곱하면 오버플로가 발생하는지 어떻게 확인할 수 있습니까?
두 숫자를 곱하면 오버플로가 발생하는 특수한 경우를 처리하고 싶습니다. 코드는 다음과 같습니다.
int a = 20;
long b = 30;
// if a or b are big enough, this result will silently overflow
long c = a * b;
그것은 단순화 된 버전입니다. 실제 프로그램에서 a
그리고 b
런타임에 다른 곳에서 소싱됩니다. 내가 달성하고 싶은 것은 다음과 같습니다.
long c;
if (a * b will overflow) {
c = Long.MAX_VALUE;
} else {
c = a * b;
}
이것을 가장 잘 코딩하는 방법은 무엇입니까?
업데이트 : a
그리고 b
내 시나리오에서 음이 아닌 항상이다.
자바 (8)이 Math.multiplyExact
, Math.addExact
int 치의과 긴 등. 이것들은 ArithmeticException
오버플 로에 체크되지 않은 것을 던집니다 .
경우 a
와 b
모두 긍정적 인 당신은 사용할 수 있습니다 :
if (a != 0 && b > Long.MAX_VALUE / a) {
// Overflow
}
양수와 음수를 모두 처리해야하는 경우 더 복잡합니다.
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
}
다음은 -10 또는 +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
긴 오버플로 / 언더 플로를 확인하는 안전한 산술 연산을 제공하는 Java 라이브러리가 있습니다. 예를 들어 Guava의 LongMath.checkedMultiply (long a, long b) 는 오버플로되지 않는 경우 a
및 의 곱을 반환하고 부호있는 산술 에서 오버플로 b
가 ArithmeticException
발생하면 throw 합니다 .a * b
long
대신 java.math.BigInteger를 사용하고 결과 크기를 확인할 수 있습니다 (코드를 테스트하지 않음).
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()
}
로그를 사용하여 결과의 크기를 확인하십시오.
Java에는 int.MaxValue와 같은 것이 있습니까? 그렇다면 시도하십시오
if (b != 0 && Math.abs(a) > Math.abs(Long.MAX_VALUE / b))
{
// it will overflow
}
수정 : 문제의 Long.MAX_VALUE 확인
jruby에서 도난 당함
long result = a * b;
if (a != 0 && result / a != b) {
// overflow
}
업데이트 :이 코드는 짧고 잘 작동합니다. 그러나 a = -1, b = Long.MIN_VALUE에 대해 실패합니다.
한 가지 가능한 개선 사항 :
long result = a * b;
if( (Math.signum(a) * Math.signum(b) != Math.signum(result)) ||
(a != 0L && result / a != b)) {
// overflow
}
Note that this will catch some overflows without any division.
Here is the simplest way I can think of
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"
}
I am not sure why nobody is looking at solution like:
if (Long.MAX_VALUE/a > b) {
// overflows
}
Choose a to be larger of the two numbers.
I'd like to build on John Kugelman's answer without replacing it by editing it directly. It works for his test case (MIN_VALUE = -10
, MAX_VALUE = 10
) because of the symmetry of MIN_VALUE == -MAX_VALUE
, which isn't the case for two's complement integers. In actuality, 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)
When applied to the true MIN_VALUE
and MAX_VALUE
, John Kugelman's answer yields an overflow case when a == -1
and b ==
anything else (point first raised by Kyle). Here's a way to fix it:
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
}
It's not a general solution for any MIN_VALUE
and MAX_VALUE
, but it is general for Java's Long
and Integer
and any value of a
and b
.
As has been pointed out, Java 8 has Math.xxxExact methods that throw exceptions on overflow.
If you are not using Java 8 for your project, you can still "borrow" their implementations which are pretty compact.
Here are some links to these implementations in the JDK source code repository, no guarantee whether these will stay valid but in any case you should be able to download the JDK source and see how they do their magic inside the java.lang.Math
class.
Math.multiplyExact(long, long)
http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/lang/Math.java#l925
Math.addExact(long, long)
http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/java/lang/Math.java#l830
etc, etc.
UPDATED: switched out invalid links to 3rd party website to links to the Mercurial repositories of Open JDK.
Maybe:
if(b!= 0 && a * b / b != a) //overflow
Not sure about this "solution".
Edit: Added b != 0.
Before you downvote: a * b / b won't be optimized. This would be compiler bug. I do still not see a case where the overflow bug can be masked.
maybe this will help you:
/**
* @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;
}
}
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)
'program story' 카테고리의 다른 글
FileList에서 파일을 제거하는 방법 (0) | 2020.08.30 |
---|---|
파일이 아닌 문자열로 CSV 형식으로 데이터를 쓰려면 어떻게해야합니까? (0) | 2020.08.30 |
DRY 방식으로 루비의 구조 절에 여러 오류 클래스 전달 (0) | 2020.08.30 |
엔티티 오류 매핑의 또 다른 반복 열 (0) | 2020.08.30 |
과거에 두 개의 임의 커밋 사이에 커밋을 삽입하는 방법은 무엇입니까? (0) | 2020.08.30 |