Java는 정수 언더플로와 오버플로우를 어떻게 처리하며 어떻게 확인합니까?
Java는 정수 언더플로우 및 오버플로우를 어떻게 처리합니까?
그 후, 이러한 현상이 발생하고 있는지 어떤 방법으로 확인/테스트하시겠습니까?
오버플로우가 발생하면 최소값으로 돌아가 거기서부터 계속됩니다.언더플로우가 발생하면 최대값으로 되돌아가 거기에서 계속됩니다.
다음과 같이 미리 확인할 수 있습니다.
public static boolean willAdditionOverflow(int left, int right) {
if (right < 0 && right != Integer.MIN_VALUE) {
return willSubtractionOverflow(left, -right);
} else {
return (~(left ^ right) & (left ^ (left + right))) < 0;
}
}
public static boolean willSubtractionOverflow(int left, int right) {
if (right < 0) {
return willAdditionOverflow(left, -right);
} else {
return ((left ^ right) & (left ^ (left - right))) < 0;
}
}
)int타타에 long long)
할 수 , 더 큰할 수 또는 하는 것을 .long또는 아마도.마지막은 오버플로가 발생하지 않습니다.실제로 사용 가능한 JVM 메모리가 한계입니다.
만약 당신이 자바8을 이미 사용하고 있다면, 당신은 새로운 방법들과 새로운 방법들을 사용할 수 있습니다.ArithmeticException넘쳐서
public static boolean willAdditionOverflow(int left, int right) {
try {
Math.addExact(left, right);
return false;
} catch (ArithmeticException e) {
return true;
}
}
public static boolean willSubtractionOverflow(int left, int right) {
try {
Math.subtractExact(left, right);
return false;
} catch (ArithmeticException e) {
return true;
}
}
,, 을, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a,boolean유틸리티 방식.
음, 초기 정수 타입에 관한 한 Java는 오버/언더플로우를 전혀 처리하지 않습니다(플로트 동작과 더블 동작은 다릅니다만, IEEE-754가 요구하는 대로 +/-무한으로 플러시 됩니다).
두 개의 int를 추가할 때 오버플로가 발생해도 아무런 표시가 나타나지 않습니다.오버플로를 확인하는 간단한 방법은 다음 큰 유형을 사용하여 실제로 작업을 수행하고 결과가 source type의 범위 내에 있는지 확인하는 것입니다.
public int addWithOverflowCheck(int a, int b) {
// the cast of a is required, to make the + work with long precision,
// if we just added (a + b) the addition would use int precision and
// the result would be cast to long afterwards!
long result = ((long) a) + b;
if (result > Integer.MAX_VALUE) {
throw new RuntimeException("Overflow occured");
} else if (result < Integer.MIN_VALUE) {
throw new RuntimeException("Underflow occured");
}
// at this point we can safely cast back to int, we checked before
// that the value will be withing int's limits
return (int) result;
}
throw 절 대신 수행할 작업은 애플리케이션 요구 사항(던지기, 플러시, 최소/최대값으로 설정 또는 로그만 기록)에 따라 달라집니다.긴 작업에서 오버플로를 탐지하려면 기본값이 부족할 경우 대신 BigInteger를 사용하십시오.
편집(2014-05-21) :이 질문은 자주 언급되는 것 같고 같은 문제를 직접 해결해야 하므로 CPU가 V 플래그를 계산하는 것과 동일한 방법으로 오버플로 상태를 평가하는 것은 매우 쉽습니다.
기본적으로 두 오퍼랜드의 부호와 결과를 포함하는 부울식입니다.
/**
* Add two int's with overflow detection (r = s + d)
*/
public static int add(final int s, final int d) throws ArithmeticException {
int r = s + d;
if (((s & d & ~r) | (~s & ~d & r)) < 0)
throw new ArithmeticException("int overflow add(" + s + ", " + d + ")");
return r;
}
자바에서는 (if의) 식을 32비트 전체에 적용하여 < 0을 사용하여 결과를 체크하는 것이 더 간단합니다(이것에 의해, 부호 비트가 효과적으로 테스트됩니다).이 원리는 모든 정수 프리미티브 타입에 대해 동일하게 동작하며, 위의 메서드의 모든 선언을 long으로 변경하면 오랫동안 동작합니다.
작은 타입의 경우 암묵적인 int로의 변환(자세한 내용은 JLS에서 비트 조작을 참조)에 의해 체크가 아닌 체크는 부호 비트를 명시적으로 마스크해야 합니다(짧은 오퍼랜드의 경우 0x8000, 바이트 오퍼랜드의 경우 0x80, 캐스트 조정 및 파라미터 선언).
/**
* Subtract two short's with overflow detection (r = d - s)
*/
public static short sub(final short d, final short s) throws ArithmeticException {
int r = d - s;
if ((((~s & d & ~r) | (s & ~d & r)) & 0x8000) != 0)
throw new ArithmeticException("short overflow sub(" + s + ", " + d + ")");
return (short) r;
}
(위의 예에서는 감산 오버플로우 검출에 필요한 표현을 사용하고 있습니다).
그러면 이러한 부울식은 어떻게, 왜 기능할까요?첫째, 어떤 논리적 사고는 두 주장의 부호가 같을 때에만 오버플로가 발생할 수 있다는 것을 보여준다.왜냐하면 한쪽 인수가 음수이고 한쪽 인수가 양수일 경우 (추가) 결과는 0에 가까워야 하며 극단적인 경우 한쪽 인수가 다른 인수와 같은 0이어야 합니다.인수 자체로는 오버플로우 조건을 만들 수 없기 때문에 합계가 오버플로우를 만들 수도 없습니다.
두 주장이 같은 부호를 가지고 있다면 어떻게 될까요?양쪽 인수가 모두 양수인 경우를 살펴보겠습니다.MAX_VALUE 유형보다 큰 합계를 작성하는2개의 인수를 추가하면 항상 음수 값이 생성됩니다.따라서 arg1 + arg2 > MAX_VALUE일 경우 오버플로가 발생합니다(두 인수의 최대값은 MAX_VALUE가 됩니다).바이트(소수)의 경우 127 + 127 = 254를 의미합니다.2개의 양의 값을 추가하면 발생할 수 있는 모든 값의 비트 표현을 보면 오버플로우(128~254)는 모두 비트7이 설정되어 있고 오버플로우(0~127)는 모두 비트7(최상단 부호)이 클리어 되어 있는 것을 알 수 있습니다.이것이 바로 표현의 첫 번째(오른쪽) 부분이 확인하는 것입니다.
if (((s & d & ~r) | (~s & ~d & r)) < 0)
(~s & ~d & r)는 양쪽 피연산자(s, d)가 양수이고 결과(r)가 음수인 경우에만 true가 됩니다(이 식은 32비트 모두에서 동작합니다만, 여기서 주목하는 유일한 비트는 최상위(부호) 비트이며, 이 비트는 < 0에 의해 체크됩니다.
두 개의 인수가 모두 음수인 경우, 그 합계는 어떤 인수보다 0에 가까울 수 없으며, 합계는 마이너스 무한대에 가까워야 합니다.생성할 수 있는 가장 극단적인 값은 MIN_VALUE + MIN_VALUE입니다. 이 값은 범위 값(-1 ~ -128)에 대해 부호 비트가 설정되고 가능한 오버플로우 값(-129 ~ -256)에는 부호 비트가 지워진 것을 나타냅니다.따라서 결과의 부호는 오버플로 상태를 다시 나타냅니다.이것이 왼쪽 절반(s & d & ~r)이 양쪽 인수(s, d)가 음수이고 결과가 양수인 경우에 체크하는 것입니다.로직은 대체로 양의 경우와 동일합니다.음수 값을 2개 추가하면 발생할 수 있는 모든 비트 패턴은 언더플로우가 발생한 경우에만 부호 비트가 지워집니다.
기본적으로는 Java의 int 및 long 산술은 오버플로 및 언더플로우에서 자동으로 랩됩니다(다른 정수 유형에 대한 정수 연산은 먼저 JLS 4.2.2에 따라 int 또는 long으로 승격됨).
의 경우 Java 8의 경우java.lang.Math는 지정된 연산을 실행하는 int 인수와 long 인수 모두에 대해, , , 및 static 메서드를 제공하며, 오버플로우에는 산술 예외(분할은 없습니다).정확한 방법--특수 케이스 1개를 체크해 주세요).MIN_VALUE / -1입니다).
Java 8에서는 java.lang.또한 긴 값이 int에 맞지 않을 경우 산술적 예외를 던지고 긴 값을 int에 캐스팅할 수도 있습니다.이것은 예를 들어 체크되지 않은 긴 산수를 사용하여 int의 합계를 계산하고, 그 다음에 다음을 사용하는 데 유용할 수 있습니다.toIntExact마지막에 int로 던지다(하지만 금액이 넘치지 않도록 주의한다).
만약 당신이 여전히 이전 버전의 Java를 사용하고 있다면, Google Guava는 체크된 덧셈, 빼기, 곱셈, 그리고 지수화를 위한 IntMath와 LongMath 정적 메서드를 제공한다.이러한 클래스는 또한 다음 값을 반환하는 요인 및 이항 계수를 계산하는 방법을 제공합니다.MAX_VALUE의 클래스인 구아바에서는SignedBytes,UnsignedBytes,Shorts ★★★★★★★★★★★★★★★★★」Ints하다, 제공하다checkedCast더 큰 유형을 좁히는 메서드(Illogal Argument를 던짐)under/overflow에 대한 예외(산술 예외 아님) 및saturatingCast「」을 하는 메서드MIN_VALUE ★★★★★★★★★★★★★★★★★」MAX_VALUE넘쳐서
Java는 int 또는 긴 프리미티브 유형에 대해 정수 오버플로우를 수행하지 않으며 양의 정수 및 음의 정수 오버플로우를 무시합니다.
이 답변에서는 먼저 정수 오버플로를 설명하고 표현식 평가에서 중간 값을 사용하더라도 발생하는 방법의 예를 제시하며 다음으로 정수 오버플로를 방지하고 검출하기 위한 자세한 기술을 제공하는 리소스에 대한 링크를 제공합니다.
예기치 않은 오버플로우 또는 검출되지 않은 오버플로우에서 정수 산술 및 식 재클러팅은 일반적인 프로그래밍 오류입니다.예기치 않은 정수 오버플로우나 검출되지 않은 정수 오버플로우는 특히 어레이, 스택 및 목록 개체에 영향을 미치기 때문에 악용 가능한 보안 문제로 잘 알려져 있습니다.
오버플로우는 양의 방향 또는 음의 방향 중 하나로 발생할 수 있으며, 양의 값 또는 음의 값이 문제의 원시 유형의 최대값 또는 최소값을 초과합니다.오버플로는 표현식 또는 연산 평가 중에 중간값으로 발생할 수 있으며 최종값이 범위 내에 있을 것으로 예상되는 표현식 또는 연산의 결과에 영향을 미칩니다.
음의 오버플로를 언더플로라고 잘못 부르는 경우가 있습니다.언더플로우는 값이 표현이 허용하는 것보다0에 가까울 때 발생합니다.언더플로우는 정수 산술에서 발생하며 예측됩니다.정수 언더플로우는 정수 평가가 -1과 0 사이 또는 0과 1 사이일 때 발생합니다.소수점 결과는 0으로 잘립니다.이는 정상이며 정수 산술에서는 예상되며 오류로 간주되지 않습니다.단, 이로 인해 예외가 발생할 수 있습니다.예를 들어 정수 언더플로우 결과가 식에서 약수로 사용되는 경우 "산술 예외: / by 0" 예외가 있습니다.
다음 코드를 고려합니다.
int bigValue = Integer.MAX_VALUE;
int x = bigValue * 2 / 5;
int y = bigValue / x;
따라서 x가 0으로 할당되고 bigValue / x의 후속 평가에서는 y가 2로 할당되는 대신 예외인 "산술 예외: / by 0"(즉, 0으로 나눗셈)이 발생합니다.
x 의 예상 결과는 858,993,458 이며, 이는 최대 int 값인 2,147,483,647 보다 작습니다.그러나 중간 결과는 Integer를 평가한 결과입니다.MAX_Value * 2는 4,294,967,294로, 이는 최대 int 값을 초과하며 2s의 보완 정수 표현에 따라 -2입니다.-2 / 5의 후속 평가는 x에 할당되는 0으로 평가됩니다.
x 계산식을 평가했을 때 다음 코드를 곱하기 전에 분할하는 식으로 재배치합니다.
int bigValue = Integer.MAX_VALUE;
int x = bigValue / 5 * 2;
int y = bigValue / x;
그러면 x는 858,993,458이 할당되고 y는 2가 할당됩니다.이것은 예상대로입니다.
bigValue / 5의 중간 결과는 429,496,729로 int의 최대값을 넘지 않습니다.429,496,729 *2의 후속 평가는 int의 최대값을 초과하지 않으며 예상 결과는 x에 할당됩니다.그러면 y에 대한 평가는 0으로 나누어지지 않습니다.x와 y에 대한 평가는 예상대로 작동합니다.
Java 정수 값은 로 저장되며 2s의 부호 있는 정수 표현에 따라 동작합니다.결과 값이 최대 또는 최소 정수 값보다 크거나 작으면 대신 2의 보완 정수 값이 됩니다.가장 일반적인 정수 산술 상황인 2s의 보완 동작을 사용하도록 명시적으로 설계되지 않은 상황에서는 결과 2s의 보완 값에 의해 위의 예시와 같이 프로그래밍 로직 또는 계산 오류가 발생합니다.뛰어난 Wikipedia 기사는 2s 보완 이진수를 다음과 같이 기술하고 있습니다.Two's complete - 위키백과
의도하지 않은 정수 오버플로를 방지하는 기술이 있습니다.테크니크는 사전 조건 테스트, 업캐스트 및 BigInteger를 사용하는 것으로 분류할 수 있습니다.
사전 조건 테스트에는 산술 연산 또는 식에 들어가는 값을 검사하여 이러한 값으로 오버플로가 발생하지 않도록 하는 것이 포함됩니다.프로그래밍 및 설계에서는 입력값이 오버플로를 일으키지 않도록 하는 테스트를 작성한 후 오버플로를 일으키는 입력값이 발생할 경우 수행할 작업을 결정해야 합니다.
업캐스팅은 보다 큰 프리미티브 타입을 사용하여 연산 또는 식을 실행하고 그 결과값이 정수의 최대값 또는 최소값을 초과하는지 여부를 판정하는 것을 포함한다.업캐스트를 사용하더라도 동작 또는 식에서 값 또는 중간값이 업캐스트타입의 최대값 또는 최소값을 초과하여 오버플로가 발생할 수 있습니다.이러한 값은 검출되지 않고 예기치 않은 결과나 바람직하지 않은 결과를 초래할 수 있습니다.분석이나 전제조건을 통해 업캐스트 없이 방지가 불가능하거나 실용적일 때 업캐스트 오버플로우를 방지할 수 있다.문제의 정수가 이미 긴 원시 유형인 경우 Java의 원시 유형에서는 업캐스팅이 불가능합니다.
BigInteger 기술은 BigInteger를 사용하는 라이브러리 메서드를 사용한 산술 연산 또는 식에 BigInteger를 사용하는 것으로 구성됩니다.BigInteger가 오버플로하지 않습니다.필요에 따라서, 사용 가능한 메모리를 모두 사용합니다.그 산술법은 보통 정수 연산보다 약간 덜 효율적입니다.BigInteger를 사용한 결과가 정수의 최대값 또는 최소값을 초과할 수 있지만 결과로 이어지는 산술에서는 오버플로가 발생하지 않습니다.프로그래밍 및 설계에서는 BigInteger 결과가 원하는 기본 결과 유형의 최대값 또는 최소값(예: int 또는 long)을 초과할 경우 수행할 작업을 결정해야 합니다.
Carnegie Mellon Software Engineering Institute의 CERT 프로그램과 Oracle은 안전한 Java 프로그래밍을 위한 일련의 표준을 만들었습니다.이 표준에는 정수 오버플로를 방지하고 검출하는 기술이 포함되어 있습니다.이 표준은 자유롭게 액세스할 수 있는 온라인 리소스로 다음과 같이 게시됩니다.자바용 CERT Oracle Secure Coding Standard
정수 오버플로를 방지하거나 검출하기 위한 코딩 기술의 실제 예를 설명하고 포함하는 표준의 섹션은 다음과 같습니다. NUM00-J. 정수 오버플로 검출 또는 방지
The CERT Oracle Secure Coding Standard for Java의 북 폼 및 PDF 폼도 이용할 수 있습니다.
이 문제에 부딪힌 지 얼마 안 된 제 해결책은 다음과 같습니다(곱셈과 덧셈 모두).
static boolean wouldOverflowOccurwhenMultiplying(int a, int b) {
// If either a or b are Integer.MIN_VALUE, then multiplying by anything other than 0 or 1 will result in overflow
if (a == 0 || b == 0) {
return false;
} else if (a > 0 && b > 0) { // both positive, non zero
return a > Integer.MAX_VALUE / b;
} else if (b < 0 && a < 0) { // both negative, non zero
return a < Integer.MAX_VALUE / b;
} else { // exactly one of a,b is negative and one is positive, neither are zero
if (b > 0) { // this last if statements protects against Integer.MIN_VALUE / -1, which in itself causes overflow.
return a < Integer.MIN_VALUE / b;
} else { // a > 0
return b < Integer.MIN_VALUE / a;
}
}
}
boolean wouldOverflowOccurWhenAdding(int a, int b) {
if (a > 0 && b > 0) {
return a > Integer.MAX_VALUE - b;
} else if (a < 0 && b < 0) {
return a < Integer.MIN_VALUE - b;
}
return false;
}
틀렸거나 간소화할 수 있다면 얼마든지 정정할 수 있습니다.나는 곱셈법으로 몇 가지 테스트를 해봤는데, 대부분 가장자리 케이스였지만, 여전히 틀릴 수 있다.
정수 오버플로/언더플로우를 체크하는 안전한 산술 연산을 제공하는 라이브러리가 있습니다.예를 들어 Guava의 IntMath.checkedAdd(int a, int b)는 다음 합계를 반환합니다.a그리고.b오버플로우하지 않는 한, 을 슬로우 합니다.ArithmeticException한다면a + b서명된 오버플로우int산술.
감겨요.
예:
public class Test {
public static void main(String[] args) {
int i = Integer.MAX_VALUE;
int j = Integer.MIN_VALUE;
System.out.println(i+1);
System.out.println(j-1);
}
}
인쇄하다
-2147483648
2147483647
java8 이후 java.lang.수학 패키지에는 다음과 같은 방법이 있습니다.addExact() ★★★★★★★★★★★★★★★★★」multiplyExact() 그그 an an an 를 던질 것이다.ArithmeticException츠요시
이런 걸 써야 할 것 같아요. Upcasting이라고 하죠.
public int multiplyBy2(int x) throws ArithmeticException {
long result = 2 * (long) x;
if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE){
throw new ArithmeticException("Integer overflow");
}
return (int) result;
}
자세한 내용은 여기를 참조하십시오: 정수 오버플로 검색 또는 방지
그것은 꽤 믿을만한 소식통이다.
아무것도 하지 않고 언더플로우/오버플로우가 발생합니다.
오버플로 연산의 결과인 "-1"은 다른 정보에서 얻은 "-1"과 다르지 않습니다.따라서 일부 상태나 값이 오버플로우인지 검사하는 것만으로는 알 수 없습니다.
그러나 중요한 경우 오버플로를 방지하거나 최소한 언제 발생할지 알 수 있도록 계산을 현명하게 수행할 수 있습니다.어떤 상황이에요?
static final int safeAdd(int left, int right)
throws ArithmeticException {
if (right > 0 ? left > Integer.MAX_VALUE - right
: left < Integer.MIN_VALUE - right) {
throw new ArithmeticException("Integer overflow");
}
return left + right;
}
static final int safeSubtract(int left, int right)
throws ArithmeticException {
if (right > 0 ? left < Integer.MIN_VALUE + right
: left > Integer.MAX_VALUE + right) {
throw new ArithmeticException("Integer overflow");
}
return left - right;
}
static final int safeMultiply(int left, int right)
throws ArithmeticException {
if (right > 0 ? left > Integer.MAX_VALUE/right
|| left < Integer.MIN_VALUE/right
: (right < -1 ? left > Integer.MIN_VALUE/right
|| left < Integer.MAX_VALUE/right
: right == -1
&& left == Integer.MIN_VALUE) ) {
throw new ArithmeticException("Integer overflow");
}
return left * right;
}
static final int safeDivide(int left, int right)
throws ArithmeticException {
if ((left == Integer.MIN_VALUE) && (right == -1)) {
throw new ArithmeticException("Integer overflow");
}
return left / right;
}
static final int safeNegate(int a) throws ArithmeticException {
if (a == Integer.MIN_VALUE) {
throw new ArithmeticException("Integer overflow");
}
return -a;
}
static final int safeAbs(int a) throws ArithmeticException {
if (a == Integer.MIN_VALUE) {
throw new ArithmeticException("Integer overflow");
}
return Math.abs(a);
}
위에 언급되지 않은 경우가 하나 있습니다.
int res = 1;
while (res != 0) {
res *= 2;
}
System.out.println(res);
작성 내용:
0
이 케이스는 다음과 같이 설명되었습니다.정수 오버플로는 0을 생성합니다.
이거면 될 것 같아요.
static boolean addWillOverFlow(int a, int b) {
return (Integer.signum(a) == Integer.signum(b)) &&
(Integer.signum(a) != Integer.signum(a+b));
}
언급URL : https://stackoverflow.com/questions/3001836/how-does-java-handle-integer-underflows-and-overflows-and-how-would-you-check-fo
'source' 카테고리의 다른 글
| Java에서 인스턴스 사용이 성능에 미치는 영향 (0) | 2022.12.24 |
|---|---|
| 1인당 및 연간 수익 계산 방법 (0) | 2022.12.12 |
| MySQL 1062 - 키 'PRIMAY'에 대한 항목 '0'이 중복 (0) | 2022.12.12 |
| python 목록 결합 (0) | 2022.12.12 |
| 배열에 대한 라라벨 컬렉션 (0) | 2022.12.12 |