부호없는 정수 곱하기 오버플로를 어떻게 감지합니까?


618

나는 a b = c 의 모든 솔루션을 찾기 위해 C ++로 프로그램을 작성 중이었습니다 . 여기서 a , bc 는 모두 0-9를 정확히 한 번만 사용합니다. 프로그램의 값 이상 반복 및 B를 , 그리고 숫자 카운팅 루틴을 실행 한 각 시간 , BB 숫자 조건이 만족되었는지를 확인하기 위해.

그러나, 스퓨리어스 솔루션을 생성 할 수 b는 정수 오버플로 제한. 나는 다음과 같은 코드를 사용하여 이것을 확인했다.

unsigned long b, c, c_test;
...
c_test=c*b;         // Possible overflow
if (c_test/b != c) {/* There has been an overflow*/}
else c=c_test;      // No overflow

오버플로를 테스트하는 더 좋은 방법이 있습니까? 일부 칩에는 오버플로가 발생할 때 설정된 내부 플래그가 있지만 C 또는 C ++을 통해 액세스하는 것을 본 적이 없습니다.


조심하십시오 서명 int 오버 플로우가 C와 C에서 정의되지 않은 동작이 ++입니다 , 그래서 당신은 실제로 발생하지 않고이를 감지해야합니다. 추가하기 전에 부호있는 int 오버 플로우에 대해서는 C / C ++에서 부호있는 오버 플로우 감지를 참조하십시오 .


21
이 주제에 유용한 정보 : Seacord의 "C 및 C ++의 보안 코딩"5 장-http : //www.informit.com/content/images/0321335724/samplechapter/seacord_ch05.pdf C ++ 용 SafeInt 클래스 -http : //blogs.msdn.com/david_leblanc/archive/2008/09/30/safeint-3-on-codeplex.aspx - http://www.codeplex.com/SafeInt IntSafe C에 대한 라이브러리 : - [ blogs.msdn .com / michael_howard / archiv
Michael Burr

3
Seacord의 보안 코딩은 훌륭한 리소스이지만 IntegerLib을 사용하지 마십시오. blog.regehr.org/archives/593을 참조하십시오 .
jww

32
gcc 컴파일러 옵션 -ftrapv을 사용하면 부호있는 정수 오버 플로우에서 SIGABRT가 생성됩니다. 여기를 참조 하십시오 .
nibot

1
오버플로 질문에 대답하지는 않지만 문제를 해결하는 또 다른 방법은 GMP 와 같은 BigNum 라이브러리를 사용하여 항상 충분한 정밀도를 보장하는 것입니다. 충분한 숫자를 미리 할당하면 오버플로에 대해 걱정할 필요가 없습니다.
wrdieter

1
@HeadGeek가 그의 답변으로 제공 한 정보는 내가 말할 것입니다. 그러나 하나의 추가로. 곱셈의 오버플로를 감지하는 방법이 아마도 가장 빠를 것입니다. HeadGeek의 답변에서 언급 한 ARM에서는 clz명령어 또는 __clz(unsigned)함수를 사용하여 숫자의 순위 (가장 높은 비트가있는 곳)를 결정할 수 있습니다. 이것이 x86 또는 x64에서 사용할 수 있는지 확실하지 않기 때문에 이것이 아니라고 가정하고 가장 중요한 비트를 찾는 데 최악의 log(sizeof(int)*8)명령 이 걸릴 것이라고 말합니다 .
nonsensickle

답변:


229

부호없는 정수를 사용하고 있습니다. 정의에 따르면 C (C ++에 대해 모른다)에서 부호없는 산술이 오버플로되지 않습니다 ... 적어도 C의 경우 포인트가 무섭습니다 :)

부호있는 정수를 사용하면 오버플로 가 발생 하면 정의되지 않은 동작 (UB)이 발생하고 프로그램이 무엇이든 할 수 있습니다 (예 : 렌더링 테스트를 결정하지 않음). 

#include <limits.h>

int a = <something>;
int x = <something>;
a += x;              /* UB */
if (a < 0) {         /* Unreliable test */
  /* ... */
}

적합한 프로그램을 만들려면 해당 오버플로 생성 하기 전에 오버플로를 테스트해야합니다 . 이 방법은 부호없는 정수와 함께 사용할 수 있습니다.

// For addition
#include <limits.h>

int a = <something>;
int x = <something>;
if ((x > 0) && (a > INT_MAX - x)) /* `a + x` would overflow */;
if ((x < 0) && (a < INT_MIN - x)) /* `a + x` would underflow */;

// For subtraction
#include <limits.h>
int a = <something>;
int x = <something>;
if ((x < 0) && (a > INT_MAX + x)) /* `a - x` would overflow */;
if ((x > 0) && (a < INT_MIN + x)) /* `a - x` would underflow */;

// For multiplication
#include <limits.h>

int a = <something>;
int x = <something>;
// There may be a need to check for -1 for two's complement machines.
// If one number is -1 and another is INT_MIN, multiplying them we get abs(INT_MIN) which is 1 higher than INT_MAX
if ((a == -1) && (x == INT_MIN)) /* `a * x` can overflow */
if ((x == -1) && (a == INT_MIN)) /* `a * x` (or `a / x`) can overflow */
// general case
if (a > INT_MAX / x) /* `a * x` would overflow */;
if ((a < INT_MIN / x)) /* `a * x` would underflow */;

합니다 (제외 부문 INT_MIN-1특별한 경우), 이상 갈 가능성이없는 INT_MININT_MAX.


97
부호없는 정수는 C ++에서도 엄격하게 오버플로되지 않습니다 (ISO / IEC 14882 : 2003 3.9.1.4). 질문에서 '오버플로'를 사용하는 것은 더 구어적인 의미로, 부호없는 유형의 잘 정의 된 줄 바꿈을 포함하려고합니다. 양의 정수가 아닌 수학 양의 정수를 나타내는 부호없는 정수에 관심이 있었기 때문에 mod 2 ^ 32 (또는 2 ^ 64). 수학적 무한 크기의 정수 행동으로부터의 편차로서 오버플로와 언어에서 정의되지 않은 동작으로 오버플로를 구별하는 것은 거의 명백하지 않은 것 같습니다.
Chris Johnson

15
그 시험 할 필요는 없습니다 x >= 0- x > 0충분합니다 (만약 x == 0다음, x + a하지 오버 플로우 분명한 이유 수 있습니다.)
caf

2
@pmg, 표준에 근거한 견적이 있습니까?
Pacerier

5
이 접근 방식이 마음에 듭니다 ... 그러나주의해야합니다. 곱셈 오버플로 감지는 포지티브 x를 가정합니다. x == 0의 경우 0 감지로 나누고 음의 x의 경우 항상 오버플로를 잘못 감지합니다.
Franz D.

4
if ((a < INT_MIN / x))테스트가 너무 늦었습니다. if (x == -1) 테스트 먼저 필요합니다.
chux-복원 Monica Monica

164

작업이 피연산자에서 가장 중요한 1의 비트의 위치와 약간의 기본적인 이진 - 수학 지식을 사용하여, 오버 플로우 가능성이 있는지 여부를 확인하는 방법.

또한 두 피연산자가 있으면 최대 피연산자의 가장 높은 1 비트보다 1 비트 더 많아집니다. 예를 들면 다음과 같습니다.

bool addition_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits<32 && b_bits<32);
}

곱셈의 경우, 두 피연산자는 피연산자의 비트 합계를 (최대) 계산합니다. 예를 들면 다음과 같습니다.

bool multiplication_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a), b_bits=highestOneBitPosition(b);
    return (a_bits+b_bits<=32);
}

마찬가지로, 당신은 결과의 최대 크기를 추정 할 수 a의 힘으로 b이 같은를 :

bool exponentiation_is_safe(uint32_t a, uint32_t b) {
    size_t a_bits=highestOneBitPosition(a);
    return (a_bits*b<=32);
}

(물론 대상 정수를 비트 수로 대체하십시오.)

숫자에서 가장 높은 1 비트의 위치를 ​​결정하는 가장 빠른 방법은 확실하지 않습니다. 다음은 무차별 대입 방법입니다.

size_t highestOneBitPosition(uint32_t a) {
    size_t bits=0;
    while (a!=0) {
        ++bits;
        a>>=1;
    };
    return bits;
}

완벽하지는 않지만 작업을 수행하기 전에 두 숫자가 넘칠 수 있는지 여부를 알 수 있습니다. highestOneBitPosition함수 의 루프로 인해 제안 된 방식으로 결과를 확인하는 것보다 더 빠를 지 여부는 알 수 없지만 (특히 피연산자에 몇 비트가 있는지 알고 있다면) 특히 그렇습니다.


98
물론 당신은 : 로그인 할 수 highestOneBitPosition의 이름을 바꿀 수
올리버 할람

37
예,와 동일한 작업 log2이지만 수학적 배경이없는 사람에게는 반드시 분명하지는 않습니다.
Geek

48
이 알고리즘은 안전한 답변을 과소 평가하지 않습니까? 가장 높은 OneBitPosition (2 ^ 31) = 32이므로 2 ^ 31 + 0은 안전하지 않은 것으로 감지됩니다. (2 ^ 32-1) * 1은 32 + 1> 32부터 안전하지 않은 것으로 감지합니다. 1 ^ 100은 1 * 100부터 안전하지 않은 것으로 감지합니다. > 32.
clahey

19
당신의 multiplication_is_safe 0x8000 * 0x10000오버플로 (비트 위치는 16 + 17 = 33 > 32 ) 0x8000 * 0x10000 = 0x80000000에 따라 부호가없는 32 비트 int에 여전히 맞지 않기 때문에 그렇지 않습니다 . 이 코드가 작동하지 않는 예제 중 하나 일뿐입니다. 0x8000 * 0x10001, ...
Michi

13
@GT_mh : 요점은? 내가 말했듯이 완벽하지는 않습니다. 그것은 어림짐작 일 때 결정적으로 말할 것입니다 이다 안전하지만, 모든 계산은 전체 계산을하지 않고 괜찮을 것입니다 여부를 판단 할 수있는 방법이 없습니다. 0x8000 * 0x10000이 정의에 따르면 "안전"하지는 않지만 괜찮습니다.
Geek

147

Clang 3.4+GCC 5+ 는 확인 된 산술 내장 기능을 제공합니다. 특히 비트 테스트 안전 점검과 비교할 때이 문제에 대한 매우 빠른 솔루션을 제공합니다.

OP의 질문에 대한 예는 다음과 같이 작동합니다.

unsigned long b, c, c_test;
if (__builtin_umull_overflow(b, c, &c_test))
{
    // Returned non-zero: there has been an overflow
}
else
{
    // Return zero: there hasn't been an overflow
}

Clang 문서는 c_test오버플로가 발생한 경우 오버플로 된 결과를 포함 할지 여부를 지정하지 않지만 GCC 문서는 그 결과를 나타냅니다. 이 두 가지가 __builtin호환 되는 것을 좋아한다면 Clang이 작동하는 방식이라고 가정하는 것이 안전 할 것입니다.

존재하는 __builtinINT 크기 긴 크기 및 긴 긴 크기 서명 부호 변형 넘칠 수있는 각각의 산술 연산 (가산, 감산, 승산), 대. 이름의 구문은 __builtin_[us](operation)(l?l?)_overflow다음과 같습니다.

  • u에 대한 서명 또는 s를위한 서명 ;
  • 동작 중 하나 add, sub또는 mul;
  • l접미사 없음 은 피연산자가 ints 임을 의미합니다 . 하나의 l수단 long; 두 ls 의미 long long합니다.

따라서 부호있는 긴 정수 덧셈의 경우는입니다 __builtin_saddl_overflow. 전체 목록은 Clang 문서 페이지 에서 찾을 수 있습니다 .

GCC 5+와 연타 3.8+ 추가로 값의 유형을 지정하지 않고 작업이 일반적인 내장 명령을 제공합니다 __builtin_add_overflow, __builtin_sub_overflow하고 __builtin_mul_overflow. 이것들은보다 작은 타입에서도 작동합니다 int.

내장은 플랫폼에 가장 적합한 것보다 낮습니다. x86에서는 캐리, 오버플로 및 부호 플래그를 확인합니다.

Visual Studio 의 cl.exe에는 해당 항목이 없습니다. 서명되지 않은 추가 및 빼기를 들어, 포함하여 <intrin.h>사용할 수 있도록 addcarry_uNN하고 subborrow_uNN(NN 같이, 비트 수입니다 addcarry_u8또는 subborrow_u64). 그들의 서명은 약간 애매하다.

unsigned char _addcarry_u32(unsigned char c_in, unsigned int src1, unsigned int src2, unsigned int *sum);
unsigned char _subborrow_u32(unsigned char b_in, unsigned int src1, unsigned int src2, unsigned int *diff);

c_in/ b_in는 입력시 carry / borrow 플래그이고 리턴 값은 출력시 carry / borrow입니다. 부호있는 연산이나 곱셈에 해당하는 것으로 보이지 않습니다.

그렇지 않으면 Windows 용 Clang이 이제 프로덕션 용으로 준비되어 있으므로 (Chrome에는 충분) 옵션이기도합니다.


__builtin_sub_overflowClang 3.4에는 없습니다.
Richard Cook

2
@RichardCook, 시간이 좀 걸렸지 만 Clang에는 3.9 버전의 일반 내장 기능이 있습니다.
zneak

@ tambre, 나는 그렇게 생각하지 않습니다.
zneak

4
에 따르면 워드 프로세서 , __builtin_add_overflow그리고 친구들은 이미 연타 3.8에 사용할 수 있습니다.
Lekensteyn

2
감사. 이것은 잘 작동합니다. Visual C ++에 해당하는 기능이 무엇입니까? 찾을 수없는 것 같습니다.
Mudit Jain

53

일부 컴파일러는 CPU에서 정수 오버플로 플래그에 대한 액세스를 제공하므로 테스트 할 수는 있지만 표준은 아닙니다.

곱셈을 수행하기 전에 오버플로 가능성을 테스트 할 수도 있습니다.

if ( b > ULONG_MAX / a ) // a * b would overflow

11
... 또는 numeric_limits <TYPE> :: max () 사용
Jonas Gulle

20
나누기 나누기를 a = 0으로 처리하는 것을 잊지 마십시오.
Thelema

16
@Thelema : "a = 0을 처리하는 것을 잊지 마십시오"-INT_MIN / -1
jww

1
만약에 b == ULONG_MAX / a? 그런 다음 잔차없이 a나누면 여전히 적합 할 수 있습니다 ULONG_MAX.
돼지

재미있게도, 성능 측면에서 곱셈은 나눗셈에 비해 다소 빠르며 모든 곱셈에 대해 나눗셈을 추가한다는 것입니다. 이 같은 소리하지 않습니다 솔루션입니다.
DrumM

40

경고 : GCC는로 컴파일 할 때 오버플로 확인을 최적화 할 수 있습니다 -O2. 이 옵션 -Wall은 다음과 같은 경우 경고를 표시합니다.

if (a + b < a) { /* Deal with overflow */ }

이 예에서는 그렇지 않습니다.

b = abs(a);
if (b < 0) { /* Deal with overflow */ }

유일한 안전한 방법은 CERT 논문에 설명 된 것처럼 오버플로가 발생하기 전에 확인하는 것인데, 이는 체계적으로 사용하는 것은 매우 지루할 것입니다.

컴파일 -fwrapv하면 문제 가 해결되지만 일부 최적화는 비활성화됩니다.

더 나은 솔루션이 절실히 필요합니다. 오버플로가 발생하지 않는 최적화를 만들 때 컴파일러가 기본적으로 경고를 발행해야한다고 생각합니다. 현재 상황에서 컴파일러는 오버플로 검사를 최적화 할 수 있습니다.


8
컴파일러는 부호있는 정수 유형으로 만이를 수행 할 수 있습니다. 부호없는 정수 유형에 대해 오버플로가 완전히 정의됩니다. 그래도 꽤 위험한 함정입니다!
SamB

1
"오버플로가 발생하지 않는 최적화를 만들 때 컴파일러에서 기본적으로 경고를 표시해야한다고 생각합니다." -그래서 for(int k = 0; k < 5; k++) {...}경고를 제기해야합니까?
user253751

2
@immibis : 왜 그래야합니까? 의 값은 k컴파일 타임에 쉽게 결정할 수 있습니다. 컴파일러는 어떤 가정도 할 필요가 없습니다.
MikeMB

2
@immibis : 위의 말을 인용하자면 : " 오버플로가 발생하지 않는 최적화만들 때 컴파일러는 기본적으로 경고를 발행해야한다고 생각합니다 ."
MikeMB

1
@MikeMB 컴파일러가 n하위 5 비트 만 사용하는 시프트 명령을 내기 전에 32 미만 인지 확인하지 않는 최적화 는 n?
user253751

30

Clang은 이제 부호있는 정수와 부호없는 정수 모두에 대해 동적 오버플로 검사를 지원합니다. -fsanitize = integer 스위치를 참조하십시오 . 현재로서는 디버그 목적으로 완전히 지원되는 동적 오버플로 검사 기능을 갖춘 유일한 C ++ 컴파일러입니다.


25

많은 사람들이 오버플로에 대한 질문에 대답 한 것을 보았습니다. 그는 문제는 모든 숫자가 반복되지 않고 사용되도록 b = c 를 찾는 것이라고 말했다 . 좋아, 그는이 게시물에서 요구 한 것이 아니지만 여전히 문제의 상한을 연구하고 그가 오버플로를 계산하거나 감지 할 필요가 없다고 결론을 내릴 필요가 있다고 생각합니다 (참고 : 능숙하지 않습니다. 수학에서는 단계별 로이 작업을 수행했지만 최종 결과는 너무 간단하여 간단한 수식을 가질 수 있습니다).

요점은 문제가 a, b 또는 c에 필요한 상한이 98.765.432라는 것입니다. 어쨌든 사소한 부분과 사소한 부분에서 문제를 나누는 것으로 시작하십시오.

  • x 0 == 1 (9, 8, 7, 6, 5, 4, 3, 2의 모든 순열은 해입니다)
  • x 1 == x (가능한 해결책 없음)
  • 0 b == 0 (가능한 해결책 없음)
  • 1 B == 1 (해결책 없음 가능)
  • a b , a> 1, b> 1 (사소하지 않음)

이제 우리는 다른 솔루션이 가능하지 않고 순열 만 유효하다는 것을 보여 주어야합니다 (그런 다음 그것을 인쇄하는 코드는 간단합니다). 우리는 상한으로 돌아갑니다. 실제로 상한은 c ≤ 98.765.432입니다. 8 자리 (각 a와 b에 대해 총 10 자리에서 1을 뺀 숫자)가 가장 큰 숫자이므로 상한입니다. 이 상한은 c에만 해당합니다. a와 b에 대한 경계는 지수 성장으로 인해 훨씬 ​​낮아야하기 때문에 b는 2에서 상한까지 다양합니다.

    9938.08^2 == 98765432
    462.241^3 == 98765432
    99.6899^4 == 98765432
    39.7119^5 == 98765432
    21.4998^6 == 98765432
    13.8703^7 == 98765432
    9.98448^8 == 98765432
    7.73196^9 == 98765432
    6.30174^10 == 98765432
    5.33068^11 == 98765432
    4.63679^12 == 98765432
    4.12069^13 == 98765432
    3.72429^14 == 98765432
    3.41172^15 == 98765432
    3.15982^16 == 98765432
    2.95305^17 == 98765432
    2.78064^18 == 98765432
    2.63493^19 == 98765432
    2.51033^20 == 98765432
    2.40268^21 == 98765432
    2.30883^22 == 98765432
    2.22634^23 == 98765432
    2.15332^24 == 98765432
    2.08826^25 == 98765432
    2.02995^26 == 98765432
    1.97741^27 == 98765432

예를 들어 마지막 줄에 주목하십시오. 1.97 ^ 27 ~ 98M입니다. 따라서 예를 들어 1 ^ 27 == 1 및 2 ^ 27 == 134.217.728이며 9 자리 (2> 1.97이므로 실제로 테스트 해야하는 것보다 큽니다)이므로 해결책이 아닙니다. 알 수 있듯이 a와 b 테스트에 사용할 수있는 조합은 실제로 작습니다. b == 14의 경우 2와 3을 시도해야합니다. b == 3의 경우 2에서 시작하여 462에서 멈 춥니 다. 모든 결과는 ~ 98M보다 작아야합니다.

이제 위의 모든 조합을 테스트하고 숫자를 반복하지 않는 조합을 찾으십시오.

    ['0', '2', '4', '5', '6', '7', '8'] 84^2 = 7056
    ['1', '2', '3', '4', '5', '8', '9'] 59^2 = 3481
    ['0', '1', '2', '3', '4', '5', '8', '9'] 59^2 = 3481 (+leading zero)
    ['1', '2', '3', '5', '8'] 8^3 = 512
    ['0', '1', '2', '3', '5', '8'] 8^3 = 512 (+leading zero)
    ['1', '2', '4', '6'] 4^2 = 16
    ['0', '1', '2', '4', '6'] 4^2 = 16 (+leading zero)
    ['1', '2', '4', '6'] 2^4 = 16
    ['0', '1', '2', '4', '6'] 2^4 = 16 (+leading zero)
    ['1', '2', '8', '9'] 9^2 = 81
    ['0', '1', '2', '8', '9'] 9^2 = 81 (+leading zero)
    ['1', '3', '4', '8'] 3^4 = 81
    ['0', '1', '3', '4', '8'] 3^4 = 81 (+leading zero)
    ['2', '3', '6', '7', '9'] 3^6 = 729
    ['0', '2', '3', '6', '7', '9'] 3^6 = 729 (+leading zero)
    ['2', '3', '8'] 2^3 = 8
    ['0', '2', '3', '8'] 2^3 = 8 (+leading zero)
    ['2', '3', '9'] 3^2 = 9
    ['0', '2', '3', '9'] 3^2 = 9 (+leading zero)
    ['2', '4', '6', '8'] 8^2 = 64
    ['0', '2', '4', '6', '8'] 8^2 = 64 (+leading zero)
    ['2', '4', '7', '9'] 7^2 = 49
    ['0', '2', '4', '7', '9'] 7^2 = 49 (+leading zero)

그들 중 어느 것도 문제와 일치하지 않습니다 ( '0', '1', ..., '9'가 없으면 볼 수 있습니다).

이를 해결하는 예제 코드는 다음과 같습니다. 또한 임의의 정밀 정수 (코드가 98 백만보다 큰 것을 계산하지 않음)가 필요하지 않기 때문에 Python으로 작성되었지만 테스트 양이 너무 적어서 고급 언어를 사용해야한다는 것을 알았습니다. 내장 컨테이너 및 라이브러리를 사용하십시오 (참고 : 코드에는 28 줄이 있습니다).

    import math

    m = 98765432
    l = []
    for i in xrange(2, 98765432):
        inv = 1.0/i
        r = m**inv
        if (r < 2.0): break
        top = int(math.floor(r))
        assert(top <= m)

        for j in xrange(2, top+1):
            s = str(i) + str(j) + str(j**i)
            l.append((sorted(s), i, j, j**i))
            assert(j**i <= m)

    l.sort()
    for s, i, j, ji in l:
        assert(ji <= m)
        ss = sorted(set(s))
        if s == ss:
            print '%s %d^%d = %d' % (s, i, j, ji)

        # Try with non significant zero somewhere
        s = ['0'] + s
        ss = sorted(set(s))
        if s == ss:
            print '%s %d^%d = %d (+leading zero)' % (s, i, j, ji)

1
왜 9.876.543.210을 상한으로 사용하지 않습니까?
Tom Roggero

3
방정식의 왼쪽에 2 자리 숫자를 사용해야하므로.
hdante

2
LHS의 값이> 1
Paul Childs

24

다음은 질문에 대한 "휴대용이 아닌"솔루션입니다. Intel x86 및 x64 CPU에는 소위 EFLAGS 레지스터가 있으며, 이는 각 정수 산술 연산 후에 프로세서에 의해 채워집니다. 자세한 설명은 생략하겠습니다. 관련 플래그는 "오버플로"플래그 (마스크 0x800) 및 "캐리"플래그 (마스크 0x1)입니다. 이를 올바르게 해석하려면 피연산자가 부호있는 유형인지 부호없는 유형인지 고려해야합니다.

다음은 C / C ++에서 플래그를 확인하는 실용적인 방법입니다. 다음 코드는 Visual Studio 2005 이상 (32 비트 및 64 비트 모두)과 GNU C / C ++ 64 비트에서 작동합니다.

#include <cstddef>
#if defined( _MSC_VER )
#include <intrin.h>
#endif

inline size_t query_intel_x86_eflags(const size_t query_bit_mask)
{
    #if defined( _MSC_VER )

        return __readeflags() & query_bit_mask;

    #elif defined( __GNUC__ )
        // This code will work only on 64-bit GNU-C machines.
        // Tested and does NOT work with Intel C++ 10.1!
        size_t eflags;
        __asm__ __volatile__(
            "pushfq \n\t"
            "pop %%rax\n\t"
            "movq %%rax, %0\n\t"
            :"=r"(eflags)
            :
            :"%rax"
            );
        return eflags & query_bit_mask;

    #else

        #pragma message("No inline assembly will work with this compiler!")
            return 0;
    #endif
}

int main(int argc, char **argv)
{
    int x = 1000000000;
    int y = 20000;
    int z = x * y;
    int f = query_intel_x86_eflags(0x801);
    printf("%X\n", f);
}

피연산자에 오버플로없이 곱한 경우에서 반환 값 0을 얻습니다 query_intel_eflags(0x801). 즉, carry 또는 overflow 플래그가 설정되지 않았습니다. 제공된 main () 예제 코드에서 오버플로가 발생하고 두 플래그가 모두 1로 설정됩니다.이 검사는 더 이상의 계산을 의미하지 않으므로 매우 빠릅니다.


21

테스트하려는 데이터 유형보다 큰 데이터 유형이있는 경우 (예 : 32 비트 추가를 수행하고 64 비트 유형이있는 경우) 오버 플로우가 발생했는지 감지합니다. 내 예는 8 비트 추가입니다. 그러나 확장 할 수 있습니다.

uint8_t x, y;    /* Give these values */
const uint16_t data16    = x + y;
const bool carry        = (data16 > 0xFF);
const bool overflow     = ((~(x ^ y)) & (x ^ data16) & 0x80);

이 페이지에 설명 된 개념을 기반으로합니다. http://www.cs.umd.edu/class/spring2003/cmsc311/Notes/Comb/overflow.html

32 비트 예를 들어 0xFF된다 0xFFFFFFFF0x80해진다 0x80000000최종적 uint16_ta가됩니다 uint64_t.

참고 : 이것은 정수 더하기 / 빼기 오버플로를 잡으며 귀하의 질문에 곱셈이 포함된다는 것을 깨달았습니다. 어떤 경우에는 분할이 최선의 방법 일 것입니다. 이것은 일반적으로 calloc구현에서 매개 변수가 곱 해져서 최종 크기를 얻음에 따라 오버플로되지 않도록 하는 방법입니다 .


링크는 분류됩니다 HTTP 403 : 금지
피터 모텐슨

18

가장 간단한 방법은 변환하는 것입니다 unsigned long으로들 unsigned long long,의 당신의 곱셈을 수행하고 0x100000000LL 그 결과를 비교합니다.

당신은 아마 당신이 당신의 예제에서 한 것처럼 나누는 것보다 더 효율적이라는 것을 알게 될 것입니다.

아, 그리고 C와 C ++에서 모두 작동합니다 (질문에 태그를 달았으므로).


glibc 매뉴얼을 살펴 보았습니다 . 의 FPE_INTOVF_TRAP일부로 정수 오버 플로우 트랩 ( )에 대한 언급이 있습니다 SIGFPE. 매뉴얼의 불쾌한 비트를 제외하고는 이상적입니다.

FPE_INTOVF_TRAP 정수 오버 플로우 (하드웨어 별 방식으로 오버 플로우 트래핑을 사용하지 않으면 C 프로그램에서는 불가능 함).

정말 부끄러운 일입니다.


4
허 .. 내가 말하지 않은 것은 이미 긴 long int를 사용하고있는 더 큰 숫자의 문제를 해결하기위한 프로그램을 작성하기 위해이 질문을하고 있다는 것입니다. long long int는 C ++ 표준에 있지 않기 때문에 혼란을 피하기 위해 32 비트 버전을 고수했습니다.
Chris Johnson

ULONG_MAX하드 코딩보다 타이핑하기 쉽고 이식하기 쉬운 것을 사용 하는 것이 0x100000000좋습니다.
jw013

24
이 작업을 할 때하지 않습니다 longlong long같은 크기 (예를 들어 많은 64 비트 컴파일러에)입니다.
interjay

오버플로에 대해 알려주는 신호에 의존하는 것은 어쨌든 속도가 느립니다.
SamB

@SamB 오버플로가 자주 예상되는 경우에만.
user253751

17

여기에 최소한 더하기에 대한 오버플로를 감지하는 가장 빠른 방법이 있습니다. 곱셈, 나눗셈 및 거듭 제곱을 이끌어 낼 수 있습니다.

아이디어는 프로세서가 값을 다시 0으로 되돌리고 C / C ++가 특정 프로세서에서 추상화되도록하기 때문에 다음과 같이 할 수 있습니다.

uint32_t x, y;
uint32_t value = x + y;
bool overflow = value < (x | y);

이것은 하나의 피연산자가 0이고 다른 피연산자가 아닌 경우 오버플로가 잘못 감지되지 않으며 이전에 제안 된 많은 NOT / XOR / AND / test 작업보다 훨씬 빠릅니다.

지적한 바와 같이,이 방법은 다른보다 정교한 방법보다 우수하지만 여전히 최적화 할 수 있습니다. 다음은 최적화를 포함하는 원본 코드의 개정판입니다.

uint32_t x, y;
uint32_t value = x + y;
const bool overflow = value < x; // Alternatively "value < y" should also work

곱셈 오버플로를 감지하는보다 효율적이고 저렴한 방법은 다음과 같습니다.

uint32_t x, y;
const bool overflow = (x >> 16U) * (y >> 16U);
uint32_t value = overflow ? UINT32_MAX : x * y;

오버플로시 UINT32_MAX 또는 곱셈 결과가 나타납니다. 이 경우 부호있는 정수에 대해 곱셈을 진행할 수 있도록 엄격하게 정의되지 않은 동작입니다.


계산 이론으로 인해 동의하지 않습니다. 다음 사항을 고려하십시오. y> x, 값 오버플로, y는 부호 비트 설정 (예 : 부호없는 문자의 경우 1 + 255) 테스트 값으로 인해 x보다 크지 만 x는 결과 in overflow = false-따라서 논리적 사용 또는이 깨진 동작을 방지하기 위해 ..
DX-MON

테스트는 제공 한 숫자 (x : = 1, y : = 255, size = uint8_t)에 대해 작동합니다. 값은 0 (1 + 255)이며 0 <1은 true입니다. 실제로 모든 숫자 쌍에 대해 작동합니다.
Gunther Piez

흠, 당신은 좋은 지적을합니다. 좋은 컴파일러는 공급자를 최적화하기 때문에 여전히 안전 측면을 고수하지만 결과가 오버플로되지 않는 "0 + 4"와 같이 오버플로되지 않는 숫자를 포함하여 모든 입력에 실제로 맞습니다.
DX-MON

4
오버플로가 있으면 x+y>=256및 보다 value=x+y-256. 때문에 y<256항상 성립 (Y-256)을 제외하고 있으므로 value < x항상 참이다. 넘치지 않는 경우에 대한 증거는 매우 유사합니다.
Gunther Piez

2
@ DX-MON : 이전 추가의 캐리 비트도있는 경우 첫 번째 방법이 필요합니다. uint32_t x[N], y[N], z[N], carry=0; for (int i = 0; i < N; i++) { z[i] = x[i] + y[i] + carry; carry = z[i] < (x[i] | y[i]); }값을 지정하지 않으면 or하나의 피연산자와 캐리 비트가 0이고 하나의 피연산자 0xffffffff와 캐리 비트가 1임을 구별 할 수 없습니다 .
Matt

14

C / C ++에서 overflow 플래그에 액세스 할 수 없습니다.

일부 컴파일러에서는 트랩 명령어를 코드에 삽입 할 수 있습니다. GCC에서 옵션은 -ftrapv입니다.

휴대용 및 컴파일러와 독립적으로 수행 할 수있는 유일한 작업은 오버플로를 직접 확인하는 것입니다. 당신의 모범에서했던 것처럼.

그러나 -ftrapv최신 GCC를 사용하는 x86에서는 아무것도하지 않는 것 같습니다. 이전 버전에서 남은 것이거나 다른 아키텍처와 관련이 있다고 생각합니다. 컴파일러가 추가 할 때마다 INTO opcode를 삽입 할 것으로 예상했습니다. 불행히도 이것은하지 않습니다.


-ftrapv는 Cygwin 상자에서 GCC 4.3.4를 사용하여 정상적으로 작동하는 것 같습니다. stackoverflow.com/questions/5005379/…에
Nate Kohl

3
둘 다 맞아 -ftrapv 서명 된 정수의 작업 만 수행
ZAB

14

부호없는 정수의 경우 결과가 인수 중 하나보다 작은 지 확인하십시오.

unsigned int r, a, b;
r = a + b;
if (r < a)
{
    // Overflow
}

부호있는 정수의 경우 인수 및 결과의 부호를 확인할 수 있습니다.

다른 부호의 정수는 오버 플로우 할 수 없으며 결과가 다른 부호의 경우에만 동일한 부호의 정수가 오버 플로우됩니다.

signed int r, a, b, s;
r = a + b;
s = a>=0;
if (s == (b>=0) && s != (r>=0))
{
    // Overflow
}

첫 번째 방법은 부호있는 정수에서도 작동하지 않습니까? char result = (char)127 + (char)3;-126입니다. 두 피연산자보다 작습니다.
primfaktor

1
아시다시피, 문제는 서명 된 유형에 대해 정의되지 않았다는 사실입니다.
primfaktor

27
부호있는 숫자의 -1 오버플로는 정의되지 않은 동작을 초래하므로 테스트가 너무 늦어서 실제로 유용하지 않습니다.
Voo

1
@primfaktor 그것은 서명 된 int에 대해 작동하지 않습니다 : char ((-127) + (-17)) = 112. 서명 된 int의 경우 인수와 결과의 부호 비트를 확인해야합니다
phuclv

3
이미 언급했듯이 오버플로의 경우 a + b의 정의되지 않은 동작으로 인해 부호있는 정수 솔루션이 작동하지 않습니다. 조작 전에 부호있는 정수로 오버 플로우를 점검 해야합니다 .
Marwan Burelle

11

비트 마스킹 및 시프트가 유망하지 않은 부동 소수점 숫자에 대해서도 동일한 질문에 대답해야했습니다. 내가 해결 한 접근 방식은 부호있는 및 부호없는 정수 및 부동 소수점 숫자에 적용됩니다. 중간 계산을 위해 더 큰 데이터 유형이없는 경우에도 작동합니다. 이러한 모든 유형에 가장 효율적인 것은 아니지만 모든 유형에 적합하므로 사용할 가치가 있습니다.

오버플로 테스트, 덧셈 및 뺄셈

  1. MAXVALUE 및 MINVALUE 유형에 대해 가능한 최대 값 및 최소값을 나타내는 상수를 확보하십시오.

  2. 피연산자의 부호를 계산하고 비교하십시오.

    ㅏ. 두 값 중 하나가 0이면 더하기 나 빼기가 오버플로 될 수 없습니다. 나머지 테스트는 건너 뜁니다.

    비. 부호가 반대이면 추가가 넘칠 수 없습니다. 나머지 테스트는 건너 뜁니다.

    씨. 부호가 같으면 빼기가 넘칠 수 없습니다. 나머지 테스트는 건너 뜁니다.

  3. MAXVALUE의 양의 오버플로를 테스트하십시오.

    ㅏ. 두 부호가 모두 양수이고 MAXVALUE-A <B이면 덧셈이 오버플로됩니다.

    비. B의 부호가 음수이고 MAXVALUE-A <-B이면 빼기가 오버플로됩니다.

  4. MINVALUE의 음의 오버플로를 테스트하십시오.

    ㅏ. 두 부호가 모두 음수이고 MINVALUE-A> B이면 더하기가 오버플로됩니다.

    비. A의 부호가 음수이고 MINVALUE-A> B이면 빼기가 오버플로됩니다.

  5. 그렇지 않으면 오버플로가 없습니다.

오버플로 테스트, 곱셈 및 나눗셈 :

  1. MAXVALUE 및 MINVALUE 유형에 대해 가능한 최대 값 및 최소값을 나타내는 상수를 확보하십시오.

  2. 피연산자의 크기 (절대 값)를 계산하고 1과 비교합니다. (아래에서 A와 B는 서명 된 원본이 아니라 이러한 크기라고 가정합니다.)

    ㅏ. 두 값 중 하나가 0이면 곱셈이 오버플로 될 수 없으며 나누기가 0 또는 무한대가됩니다.

    비. 두 값 중 하나가 하나이면 곱셈과 나눗셈이 오버플로 될 수 없습니다.

    씨. 한 피연산자의 크기가 1보다 낮고 다른 피연산자의 크기가 1보다 크면 곱셈이 오버플로 될 수 없습니다.

    디. 크기가 둘 다 작은 경우 나누기가 오버플로 될 수 없습니다.

  3. MAXVALUE의 양의 오버플로를 테스트하십시오.

    ㅏ. 두 피연산자가 모두 1보다 크고 MAXVALUE / A <B이면 곱셈이 오버플로됩니다.

    비. B가 1보다 작고 MAXVALUE * B <A이면 나누기가 오버플로됩니다.

  4. 그렇지 않으면 오버플로가 없습니다.

참고 : MINVALUE의 최소 오버플로는 3으로 처리됩니다. 절대 값을 취했기 때문입니다. 그러나 ABS (MINVALUE)> MAXVALUE이면 약간의 오 탐지가 발생합니다.

언더 플로에 대한 테스트는 비슷하지만 EPSILON (0보다 작은 양수)이 포함됩니다.


1
POSIX 시스템에서는 SIGFPE 신호를 부동 소수점 언더 플로우 / 오버 플로우에 사용할 수 있습니다.
Chris Johnson

부동 소수점으로 변환하고 다시 작동하지만 다른 솔루션보다 훨씬 느립니다 (32 비트 시스템에서 테스트 한 결과).
JanKanis

검토자가 빼기 파트 2에 대해 누락 된 사례를 발견했습니다. 0-MINVALUE가 오버 플로우됨에 동의합니다. 따라서이 경우에 대한 테스트가 추가되어야합니다.
Paul Chernoch

<pedantic> 정수는 언더 플로되지 않습니다 (= 0에 너무 가까워 져서 정확하게 표현할 수 없음). 1.0e-200 / 1.0e200IEEE 배가를 가정하면 실제 언더 플로의 예입니다. 대신 올바른 용어는 음의 오버플로입니다. </ pedantic>
Arne Vogel

정확히 말해, 정수가 언더 플로로 간주되지 않는 이유는 정의 된 잘림 동작 때문입니다. 예를 들어 1/INT_MAX언더 플로로 간주 될 수 있지만 언어는 단순히 잘림을 0으로 지정합니다.
Arne Vogel

8

CERT는 "as-if"무한 범위 (AIR) 정수 모델을 사용하여 부호있는 정수 오버플로, 부호없는 정수 줄 바꿈 및 정수 잘림을 감지하고보고하는 새로운 방법을 개발했습니다. CERT는 모델을 설명 하는 기술 보고서 를 게시하고 GCC 4.4.0 및 GCC 4.5.0을 기반으로 작동하는 프로토 타입을 제작했습니다.

AIR 정수 모델은 무한 범위의 정수를 사용하여 얻은 것과 동등한 값을 생성하거나 런타임 제약 조건 위반을 초래합니다. 이전 정수 모델과 달리 AIR 정수에는 정확한 트랩이 필요하지 않으므로 대부분의 기존 최적화가 중단되거나 금지되지 않습니다.


나는 링크에서 유용한 것을 보지 못했지만 그것은 내가 오랫동안 옹호 한 모델처럼 들린다. 그것은 대부분의 유용한 최적화를 지원하는 동시에 대부분의 구현이 본질적으로 무료로 제공 할 수있는 유용한 의미 론적 보장을 지원합니다. 코드가 함수에 대한 입력 이 출력이 중요한 모든 경우에 유효하다는 것을 알고 있지만 출력이 중요한지 미리 알지 못하면 아무 영향을 미치지 않는 경우 오버플로가 발생할 수 있습니다. 모든 비용을 방지하는 것보다 쉽고 효율적입니다.
supercat

8

또 다른 흥미로운 도구는 IOC : C / C ++ 용 정수 오버플로 검사기입니다 .

이것은 패치 된 Clang 컴파일러이며 컴파일 타임에 코드에 검사를 추가합니다.

다음과 같이 출력됩니다.

CLANG ARITHMETIC UNDEFINED at <add.c, (9:11)> :
Op: +, Reason : Signed Addition Overflow,
BINARY OPERATION: left (int32): 2147483647 right (int32): 1

1
이 패치는 이제 다른 살균제 중에서 clang 코드베이스로 병합되었습니다.
ZAB

7

어셈블리 언어를 사용하는 솔루션의 다른 변형은 외부 절차입니다. Linux x64에서 g ++ 및 fasm을 사용하여 부호없는 정수 곱셈에 대한이 예입니다.

이 절차는 부호없는 정수 인수 (32 비트)를 곱합니다 ( mdd64 사양 (섹션 3.2.3 매개 변수 전달 ) 에 따름 ).

클래스가 INTEGER 인 경우 시퀀스 % rdi, % rsi, % rdx, % rcx, % r8 및 % r9의 다음 사용 가능한 레지스터가 사용됩니다.

(내 코드에 EDI 및 esi 레지스터)) 결과를 반환하거나 오버플로가 발생하면 0을 반환합니다.

format ELF64

section '.text' executable

public u_mul

u_mul:
  MOV eax, edi
  mul esi
  jnc u_mul_ret
  xor eax, eax
u_mul_ret:
ret

테스트:

extern "C" unsigned int u_mul(const unsigned int a, const unsigned int b);

int main() {
    printf("%u\n", u_mul(4000000000,2)); // 0
    printf("%u\n", u_mul(UINT_MAX/2,2)); // OK
    return 0;
}

프로그램을 asm 오브젝트 파일과 링크하십시오. 필자의 경우 Qt CreatorLIBS 에서 .pro 파일에 추가 하십시오.


5

복식으로 결과를 계산하십시오. 유효 숫자는 15 자리입니다. 귀하의 요구 사항은 하드 상단에 바인딩이 C (10)의 8  - 대부분의 8 개 자리에서 할 수 있습니다. 따라서 결과가 범위 내에 있으면 정확하고 그렇지 않으면 오버플로되지 않습니다.


5

이 매크로를 사용하여 32 비트 시스템의 오버플로 비트를 테스트하십시오 (Angel Sinigersky 솔루션에 적합).

#define overflowflag(isOverflow){   \
size_t eflags;                      \
asm ("pushfl ;"                     \
     "pop %%eax"                    \
    : "=a" (eflags));               \
isOverflow = (eflags >> 11) & 1;}

오버플로 비트를 덮어 썼기 때문에 매크로로 정의했습니다.

다음은 위의 코드 단계를 가진 작은 응용 프로그램입니다.

#include <cstddef>
#include <stdio.h>
#include <iostream>
#include <conio.h>
#if defined( _MSC_VER )
#include <intrin.h>
#include <oskit/x86>
#endif

using namespace std;

#define detectOverflow(isOverflow){     \
size_t eflags;                      \
asm ("pushfl ;"                     \
    "pop %%eax"                     \
    : "=a" (eflags));               \
isOverflow = (eflags >> 11) & 1;}

int main(int argc, char **argv) {

    bool endTest = false;
    bool isOverflow;

    do {
        cout << "Enter two intergers" << endl;
        int x = 0;
        int y = 0;
        cin.clear();
        cin >> x >> y;
        int z = x * y;
        detectOverflow(isOverflow)
        printf("\nThe result is: %d", z);
        if (!isOverflow) {
            std::cout << ": no overflow occured\n" << std::endl;
        } else {
            std::cout << ": overflow occured\n" << std::endl;
        }

        z = x * x * y;
        detectOverflow(isOverflow)
        printf("\nThe result is: %d", z);
        if (!isOverflow) {
            std::cout << ": no overflow ocurred\n" << std::endl;
        } else {
            std::cout << ": overflow occured\n" << std::endl;
        }

        cout << "Do you want to stop? (Enter \"y\" or \"Y)" << endl;

        char c = 0;

        do {
            c = getchar();
        } while ((c == '\n') && (c != EOF));

        if (c == 'y' || c == 'Y') {
            endTest = true;
        }

        do {
            c = getchar();
        } while ((c != '\n') && (c != EOF));

    } while (!endTest);
}

4
모든 32 비트 시스템이 Intel x86과 호환되는 것은 아니며 모든 컴파일러가 gnu 어셈블리 구문을 지원하지는 않습니다 ( _MSC_VERMS 컴파일이 모두 코드를 거부하지만 테스트하는 코드를 게시하는 것이 재미있다 ).
Ben Voigt


2

C / C ++에서 overflow 플래그에 액세스 할 수 없습니다.

나는 이것에 동의하지 않습니다. 인라인 어셈블리 언어를 작성하고 jox86을 사용하여 오버플로를 트랩한다고 가정 하고 (점프 오버플로) 명령을 사용할 수 있습니다. 물론 코드는 더 이상 다른 아키텍처로 이식 할 수 없습니다.

info asinfo gcc.


8
인라인 어셈블러는 C / C ++ 기능이 아니며 플랫폼 독립적입니다. x86에서는 브랜치 btw 대신 into 명령어를 사용할 수 있습니다.
Nils Pipenbrinck

0

Head Geek의 답변을 확장하려면 더 빠른 방법이 있습니다 addition_is_safe.

bool addition_is_safe(unsigned int a, unsigned int b)
{
    unsigned int L_Mask = std::numeric_limits<unsigned int>::max();
    L_Mask >>= 1;
    L_Mask = ~L_Mask;

    a &= L_Mask;
    b &= L_Mask;

    return ( a == 0 || b == 0 );
}

이것은 64 비트 및 32 비트 부호없는 정수가 여전히 잘 작동한다는 점에서 기계 아키텍처 안전을 사용합니다. 기본적으로 가장 중요한 비트를 제외한 모든 마스크를 마스크하는 마스크를 만듭니다. 그런 다음 두 정수를 모두 마스킹하고 둘 중 하나에 해당 비트 세트가 없으면 추가가 안전합니다.

마스크가 변경되지 않기 때문에 일부 생성자에서 마스크를 사전 초기화하면 훨씬 빠릅니다.


5
이것은 정확하지 않습니다. 캐리는 낮은 위치에서 비트를 가져와 오버플로를 유발할 수 있습니다. 추가를 고려하십시오 UINT_MAX + 1. 마스킹 후에 a는 높은 비트가 설정되지만 10이되므로 함수가 반환 true되며 추가는 안전하지만 오버플로로 직접 이동합니다.
돼지

0

mozilla::CheckedInt<T>정수 유형에 대한 오버플로 검사 정수 수학을 제공합니다 T(가능한 경우 clang 및 gcc에서 컴파일러 내장 함수 사용). 코드는 MPL 2.0 아래 세에 따라 ( IntegerTypeTraits.h, Attributes.hCompiler.h) 다른 헤더 만이 아닌 표준 라이브러리 헤더 플러스 모질라 고유의 주장 기계 . 코드를 가져올 경우 어설 션 기계를 교체 할 수 있습니다.


-1

MSalter의 답변 은 좋은 생각입니다.

정수 계산이 필요하지만 (정밀도) 부동 소수점을 사용할 수있는 경우 다음과 같은 작업을 수행 할 수 있습니다.

uint64_t foo(uint64_t a, uint64_t b) {
    double dc;

    dc = pow(a, b);

    if (dc < UINT_MAX) {
       return (powu64(a, b));
    }
    else {
      // Overflow
    }
}

일반적으로 부동 소수점에서 계산을 반복하는 것은 나쁜 생각이지만 이 특정 지수 a ^ c의 경우 더 효율적일 수 있습니다. 그러나 테스트는 다음 (c * log(a) < max_log)과 같아야합니다 .const double max_log = log(UINT_MAX)
Toby Speight

-1

x86 명령어 세트에는 결과를 두 레지스터에 저장하는 부호없는 곱하기 명령어가 포함됩니다. C에서 해당 명령어를 사용하려면 64 비트 프로그램 (GCC)에 다음 코드를 작성할 수 있습니다.

unsigned long checked_imul(unsigned long a, unsigned long b) {
  unsigned __int128 res = (unsigned __int128)a * b;
  if ((unsigned long)(res >> 64))
    printf("overflow in integer multiply");
  return (unsigned long)res;
}

32 비트 프로그램의 경우 결과를 64 비트 및 매개 변수를 32 비트로 만들어야합니다.

대안은 컴파일러 종속 내장 함수를 사용하여 플래그 레지스터를 확인하는 것입니다. 오버 플로우 고유성에 대한 GCC 문서는 오버 플로우 검사로 산술을 수행하는 6.56 내장 함수 에서 찾을 수 있습니다 .


1
부호없는 __uint128오버플로와 오른쪽으로 음수 값을 이동하지 않으 려면 부호없는 128 비트 유형 을 사용해야합니다 .
chqrlie

무엇 "컴파일러에 의존하는 본능""오버 플로우 본능" ? " 내장 함수 " 를 의미 합니까? 당신은 참조가 있습니까? (가 응답하시기 바랍니다 답변을 편집 ) 적절한 (코멘트하지 여기에.)
피터 모텐슨

-3
#include <stdio.h>
#include <stdlib.h>

#define MAX 100 

int mltovf(int a, int b)
{
    if (a && b) return abs(a) > MAX/abs(b);
    else return 0;
}

main()
{
    int a, b;

    for (a = 0; a <= MAX; a++)
        for (b = 0; b < MAX; b++) {

        if (mltovf(a, b) != (a*b > MAX)) 
            printf("Bad calculation: a: %d b: %d\n", a, b);

    }
}

-3

이를 수행하는 확실한 방법은 모든 연산자 (특히 + 및 *)를 재정의하고 작업을 수행하기 전에 오버플로를 확인하는 것입니다.


6
내장 유형의 연산자를 재정의 할 수 없다는 점을 제외하고. 이를 위해 클래스를 작성하고이를 사용하려면 클라이언트 코드를 다시 작성해야합니다.
Blaisorblade

-3

사용 용도에 따라 다릅니다. 부호없는 long (DWORD) 덧셈 또는 곱셈을 수행하는 가장 좋은 방법은 ULARGE_INTEGER를 사용하는 것입니다.

ULARGE_INTEGER는 두 DWORD의 구조입니다. 높은 DWORD는 "HighPart"로 액세스하고 낮은 DWORD는 "LowPart"로 액세스하는 동안 전체 값은 "QuadPart"로 액세스 할 수 있습니다.

예를 들면 다음과 같습니다.

DWORD
My Addition(DWORD Value_A, DWORD Value_B)
{
    ULARGE_INTEGER a, b;

    b.LowPart = Value_A;  // A 32 bit value(up to 32 bit)
    b.HighPart = 0;
    a.LowPart = Value_B;  // A 32 bit value(up to 32 bit)
    a.HighPart = 0;

    a.QuadPart += b.QuadPart;

    // If  a.HighPart
    // Then a.HighPart contains the overflow (carry)

    return (a.LowPart + a.HighPart)

    // Any overflow is stored in a.HighPart (up to 32 bits)

6
불행히도 이것은 Windows 전용 솔루션입니다. 다른 플랫폼에는 없습니다 ULARGE_INTEGER.
Mysticial

-3

이식 가능한 방식으로 오버플로하지 않고 부호없는 곱셈을 수행하려면 다음을 사용할 수 있습니다.

... /* begin multiplication */
unsigned multiplicand, multiplier, product, productHalf;
int zeroesMultiplicand, zeroesMultiplier;
zeroesMultiplicand = number_of_leading_zeroes( multiplicand );
zeroesMultiplier   = number_of_leading_zeroes( multiplier );
if( zeroesMultiplicand + zeroesMultiplier <= 30 ) goto overflow;
productHalf = multiplicand * ( c >> 1 );
if( (int)productHalf < 0 ) goto overflow;
product = productHalf * 2;
if( multiplier & 1 ){
   product += multiplicand;
   if( product < multiplicand ) goto overflow;
}
..../* continue code here where "product" is the correct product */
....
overflow: /* put overflow handling code here */

int number_of_leading_zeroes( unsigned value ){
   int ctZeroes;
   if( value == 0 ) return 32;
   ctZeroes = 1;
   if( ( value >> 16 ) == 0 ){ ctZeroes += 16; value = value << 16; }
   if( ( value >> 24 ) == 0 ){ ctZeroes +=  8; value = value <<  8; }
   if( ( value >> 28 ) == 0 ){ ctZeroes +=  4; value = value <<  4; }
   if( ( value >> 30 ) == 0 ){ ctZeroes +=  2; value = value <<  2; }
   ctZeroes -= x >> 31;
   return ctZeroes;
}

-4

오버플로를 테스트하는 간단한 방법은 현재 값이 이전 값보다 작은 지 확인하여 유효성 검사를 수행하는 것입니다. 예를 들어, 2의 거듭 제곱을 인쇄하는 루프가 있다고 가정합니다.

long lng;
int n;
for (n = 0; n < 34; ++n)
{
   lng = pow (2, n);
   printf ("%li\n", lng);
}

내가 설명한 방식으로 오버플로 검사를 추가하면 다음과 같은 결과가 발생합니다.

long signed lng, lng_prev = 0;
int n;
for (n = 0; n < 34; ++n)
{
    lng = pow (2, n);
    if (lng <= lng_prev)
    {
        printf ("Overflow: %i\n", n);
        /* Do whatever you do in the event of overflow.  */
    }
    printf ("%li\n", lng);
    lng_prev = lng;
}

양수 및 음수 값뿐만 아니라 부호없는 값에도 작동합니다.

물론, 값을 늘리는 대신 값을 낮추는 비슷한 작업을 원한다면 언더 플로의 동작이 오버플로의 동작과 같다고 가정하여 <=부호를 뒤집습니다 >=. 정직하게 말하면 CPU의 오버플로 플래그에 액세스하지 않고 이식성이 좋으며 인라인 어셈블리 코드가 필요하므로 구현에서 코드를 이식 할 수 없습니다.


9
부호있는 값이 오버플로되면 프로그램 동작이 정의되지 않습니다. 감싸는 것은 보장되지 않습니다.
David Stone
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.