하는지 어떻게 확인할 수 있 곱하여 두 번호 Java 는 원인이 될 것이는 오버플로우?
-
11-09-2019 - |
문제
내가 처리하는 특별한 경우를 곱하여 두 번호 함께 일으키는 오버플로우가 발생합니다.코드는 다음과 같이 나타납니다.
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
는 항상 음수가 아닌 내 시나리오에.
해결책
Java 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 라이브러리가 있으며 오버플로/언더 플로우를 확인합니다. 예를 들어, 구아바 longmath.checkedmultiply (Long A, Long B) 제품을 반환합니다 a
그리고 b
, 그것이 오버플로되지 않고 던지면 ArithmeticException
만약에 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
}
이것은 분열없이 오버플로를 잡을 것입니다.
여기에 내가 생각할 수있는 가장 간단한 방법이 있습니다
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"
}
왜 아무도 다음과 같은 솔루션을보고 있는지 잘 모르겠습니다.
if (Long.MAX_VALUE/a > b) {
// overflows
}
두 숫자 중 더 큰 A를 선택하십시오.
John Kugelman의 답변을 직접 편집하여 대체하지 않고 바탕으로 구축하고 싶습니다. 그것은 그의 테스트 사례에서 작동합니다 (MIN_VALUE = -10
, MAX_VALUE = 10
)의 대칭 때문에 MIN_VALUE == -MAX_VALUE
, 이것은 2의 보완 정수의 경우가 아닙니다. 실제로, 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)
참에 적용될 때 MIN_VALUE
그리고 MAX_VALUE
, John Kugelman의 답변은 오버 플로우 케이스를 생성합니다. a == -1
그리고 b ==
다른 것 (카일에 의해 처음 자란 점). 다음은이를 해결하는 방법입니다.
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
}
그것은 어떤 일반적인 해결책이 아닙니다 MIN_VALUE
그리고 MAX_VALUE
, 그러나 그것은 Java 's에게는 일반적입니다 Long
그리고 Integer
그리고 모든 가치 a
그리고 b
.
아마도:
if(b!= 0 && a * b / b != a) //overflow
이 "솔루션"에 대해 잘 모르겠습니다.
편집 : 추가 b! = 0.
당신이 내리기 전에: A * B / B는 최적화되지 않습니다. 이것은 컴파일러 버그입니다. 오버플로 버그를 가릴 수있는 경우가 여전히 보이지 않습니다.
어쩌면 이것은 당신에게 도움이 될 것입니다 :
/**
* @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;
}
}
지적한 바와 같이, Java 8에는 Math.xxxexact 메소드가 오버플로에 예외를 제외합니다.
프로젝트에 Java 8을 사용하지 않는 경우 여전히 꽤 컴팩트 한 구현을 "빌려"할 수 있습니다.
다음은 제 3 자 웹 사이트에서 이러한 구현에 대한 링크입니다. 이들은 유효한 상태를 유지하는지 여부를 보장하지 않지만 어쨌든 JDK 소스로 들어가서 그들이 어떻게 마술을 어떻게하는지 확인할 수 있어야합니다. 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
등 등
C / C ++ (긴 * 긴) :
const int64_ w = (int64_) a * (int64_) b;
if ((long) (w >> sizeof(long) * 8) != (long) w >> (sizeof(long) * 8 - 1))
// overflow
java (int * int, java에서 int64를 찾지 못했습니다) :
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. 결과를 큰 유형으로 구하십시오 (int*int 결과를 길고 긴*긴*int64에 넣습니다)
2.CMP 결과 >> 비트 및 결과 >> (비트 -1)