부호없는 정수 오버플로가 동작을 정의하지만 부호있는 정수 오버플로는 왜 그렇지 않습니까?


209

부호없는 정수 오버플로는 C 및 C ++ 표준에 의해 잘 정의됩니다. 예를 들어 C99 표준 ( §6.2.5/9) 상태

결과 부호없는 정수 유형으로 표현할 수없는 결과는 결과 유형으로 표현할 수있는 가장 큰 값보다 1이 큰 수의 모듈로 감소되기 때문에 부호없는 피연산자가 포함 된 계산은 절대로 흐르지 않습니다.

그러나 두 표준 모두 부호있는 정수 오버플로는 정의되지 않은 동작이라고 명시하고 있습니다. 다시 C99 표준 ( §3.4.3/1)에서

정의되지 않은 동작의 예는 정수 오버 플로우에서의 동작입니다.

이 불일치에 대한 역사적 또는 더 나은 기술적 이유가 있습니까?


50
부호있는 정수를 나타내는 여러 가지 방법이 있기 때문일 수 있습니다. 적어도 C ++에서는 표준에 지정되지 않은 방법입니다.
juanchopanza


7
juanchopanza가 말한 것이 의미가 있습니다. 내가 이해하는 것처럼, 대부분의 원래 C 표준은 기존 관행을 체계화했습니다. 당시의 모든 구현이 서명되지 않은 "오버플로"가 무엇을해야하는지에 동의 한 경우이를 표준화해야하는 좋은 이유입니다. 그들은 서명 된 오버플로가 무엇을 해야하는지에 동의하지 않았으므로 표준에 미치지 못했습니다.

2
@DavidElliman 추가시 서명되지 않은 랩 어라운드도 쉽게 감지 할 수 있습니다 ( if (a + b < a)). 곱하기의 오버플로는 부호있는 유형과 부호없는 유형 모두에 어렵습니다.

5
@DavidElliman : 감지 할 수 있는지 여부뿐만 아니라 결과가 무엇인지에 대한 문제입니다. 부호 + 값 구현에서 MAX_INT+1 == -0, 두의에이 될 것이다 보완하면서INT_MIN
dribeas - 데이비드 로드리게스

답변:


163

역사적 이유는 대부분의 C 구현 (컴파일러)이 사용 된 정수 표현으로 구현하기 가장 쉬운 오버 플로우 동작을 사용했기 때문입니다. C 구현은 일반적으로 CPU에서 사용하는 것과 동일한 표현을 사용하므로 오버플로 동작은 CPU에서 사용하는 정수 표현을 따릅니다.

실제로는 구현에 따라 다를 수있는 부호있는 값의 표현 일뿐입니다. 1의 보수, 2의 보수, 부호 크기. 부호없는 유형의 경우 표준이 이진 표현을 하나만 나타 내기 때문에 표준이 변형을 허용 할 이유가 없습니다 (표준은 이진 표현 만 허용 함).

관련 인용문 :

C99 6.2.6.1:3 :

부호없는 비트 필드와 부호없는 char 유형의 객체에 저장된 값은 순수한 이진 표기법을 사용하여 표현해야합니다.

C99 6.2.6.2:2 :

부호 비트가 1 인 경우 다음 방법 중 하나로 값을 수정해야합니다.

-부호 비트가 0 인 대응 값이 무효화 됨 ( 부호 및 크기 );

-부호 비트는-(2 N ) ( 2의 보수 ) 값을 갖는다.

— 부호 비트의 값은 − (2 N -1)입니다 ( 1의 보수 ).


오늘날 모든 프로세서는 2의 보수 표현을 사용하지만 부호있는 산술 오버플로는 정의되지 않은 상태로 유지되며 컴파일러 제조업체는이 정의되지 않은 정보를 사용하여 최적화에 도움이되므로 정의되지 않은 상태로 유지하려고합니다. 예를 들어 이안 랜스 테일러 (Ian Lance Taylor)의 블로그 게시물 또는 Agner Fog의 불만 사항 및 버그 보고서에 대한 답변을 참조하십시오.


6
그러나 여기서 중요한 점 은 현대 세계에는 2의 보수 부호 산술 이외의 것을 사용하는 아키텍처 가 없다는 것입니다. 언어 표준이 여전히 PDP-1에서 구현을 허용한다는 것은 순수한 역사적 유물입니다.
Andy Ross

9
@AndyRoss이지만 2013 년 현재 보완 및 새로운 릴리스를 갖춘 시스템 (OS + 컴파일러, 오래된 이력이 있음)이 여전히 있습니다. 예 : OS
2200.

3
@ 앤디 로스는 "아키텍쳐가 아닌 ... 2의 보수 이외의 것을 사용하는 것 ..."을 오늘 고려한다면 DSP와 임베디드 프로세서의 영역이 포함됩니까?
chux-복원 모니카

11
@AndyRoss : 2s 보수 이외의 것을 사용하는 "no"아키텍처가 있지만 ( "no"의 일부 정의에 대해서는) 부호있는 정수에 대해 포화 산술을 사용 하는 DSP 아키텍처가 있습니다.
Stephen Canon

10
포화 부호있는 산술은 표준을 확실히 준수합니다. 물론 래핑 명령어는 부호없는 산술에 사용해야하지만 컴파일러는 부호없는 또는 부호있는 산술이 수행되는지 여부를 알 수있는 정보를 항상 가지고 있으므로 지침을 적절히 선택할 수 있습니다.
caf

15

파스칼의 좋은 대답 (주된 동기라고 확신합니다) 외에도 일부 프로세서는 부호있는 정수 오버플로에 예외를 일으킬 수 있습니다. 물론 컴파일러가 "다른 행동을 취해야"한다면 문제가 발생할 수 있습니다. 예를 들어 여분의 명령을 사용하여 잠재적 오버플로를 확인하고이 경우 다르게 계산합니다.

"정의되지 않은 행동"이 "작동하지 않는다"는 의미는 아닙니다. 그것은 구현이 그 상황에서 좋아하는 것을 할 수 있음을 의미합니다. 여기에는 "올바른 일"과 "경찰 부름"또는 "충돌"이 포함됩니다. 가능하면 대부분의 컴파일러는 정의하기가 비교적 쉽다고 가정 할 때 "올바른 일을"선택합니다. 그러나 계산에 오버플로가 발생하면 실제로 결과가 무엇인지 이해하고 컴파일러가 예상 한 것 이외의 작업을 수행 할 수 있습니다 (컴파일러 버전, 최적화 설정 등에 따라 다를 수 있음) .


23
컴파일러는 올바른 작업을 수행하는 데 의존하지 않기를 원 int f(int x) { return x+1>x; }하며 최적화로 컴파일하자마자 대부분을 보여줍니다 . GCC와 ICC는 기본 옵션을 사용하여 위의 값을로 최적화합니다 return 1;.
Pascal Cuoq

1
int최적화 수준에 따라 오버플로가 발생했을 때 다른 결과를 제공하는 예제 프로그램 은 ideone.com/cki8nM을 참조하십시오. 이것이 귀하의 답변이 나쁜 조언을한다는 것을 보여줍니다.
Magnus Hoff

나는 그 부분을 약간 수정했다.
매트 피터슨

C가 "래핑 부호있는 2의 보수"정수를 선언하는 수단을 제공하는 경우, C를 전혀 실행할 수있는 플랫폼은 적어도 어느 정도 효율적으로 C를 지원하는 데 큰 어려움이 없어야합니다. 래핑 동작이 필요하지 않은 경우 코드에서 이러한 유형을 사용하지 않아야하는 추가 오버 헤드가 충분하지만 2의 보수 정수에 대한 대부분의 연산은 비교 및 ​​승격을 제외하고 부호없는 정수의 연산과 동일합니다.
supercat

1
음수 값이 존재해야하고 컴파일러가 올바르게 작동하려면 "작동"해야합니다. 물론 프로세서 내에서 부호있는 값이 부족하면이를 해결하고 부호없는 값을 1의 보수 또는 2의 보수로 사용할 수 있습니다. 명령어 세트가 무엇인지에 따라 감지합니다. 일반적으로 하드웨어를 지원하는 것보다이 작업을 수행하는 것이 훨씬 느리지 만 하드웨어에서 부동 소수점을 지원하지 않거나 유사한 프로세서와 다르지 않습니다. 많은 추가 코드 만 추가하면됩니다.
Mats Petersson

10

우선, C11 3.4.3은 모든 예제 및 각주와 마찬가지로 규범적인 텍스트가 아니므로 인용과 관련이 없습니다!

정수와 부동의 오버플로가 정의되지 않은 동작이라는 내용의 관련 텍스트는 다음과 같습니다.

C11 6.5 / 5

표현식을 평가하는 동안 예외 조건이 발생하는 경우 (즉, 결과가 수학적으로 정의되지 않았거나 해당 유형의 표현 가능한 값 범위에없는 경우) 동작은 정의되지 않습니다.

부호없는 정수 유형의 동작에 대한 설명은 구체적으로 여기에서 찾을 수 있습니다.

C11 6.2.5 / 9

부호있는 정수 유형의 음이 아닌 값의 범위는 해당 부호없는 정수 유형의 하위 범위이며 각 유형에서 동일한 값의 표현은 동일합니다. 부호없는 피연산자가 포함 된 계산은 결과 부호없는 정수 유형으로 표현할 수없는 결과가 결과 유형으로 표현할 수있는 가장 큰 값보다 1이 큰 모듈로 감소되므로 오버 플로우 할 수 없습니다.

이것은 부호없는 정수 타입을 특별한 경우로 만듭니다.

또한 모든 유형이 부호 있는 유형으로 변환 되고 이전 값을 더 이상 표시 할 수없는 경우 예외가 있습니다. 신호가 발생하더라도 동작은 구현에 따라 정의됩니다.

C11 6.3.1.3

6.3.1.3 부호있는 정수와 부호없는 정수

정수 유형의 값이 _Bool 이외의 다른 정수 유형으로 변환 될 때 새 유형으로 값을 표시 할 수 있으면 변경되지 않습니다.

그렇지 않으면 새 유형에 부호가없는 경우 값이 새 유형의 범위에 올 때까지 새 유형에 표시 될 수있는 최대 값보다 하나 이상을 반복적으로 더하거나 빼서 값이 변환됩니다.

그렇지 않으면 새 유형이 서명되고 값을 표시 할 수 없습니다. 결과는 구현 정의되거나 구현 정의 신호가 발생합니다.


6

다른 문제뿐만 아니라 부호 수학 랩 추상 대수 그룹으로 (값의 쌍에 대해, 무엇보다도 그 의미의 행동 부호없는 정수 타입하게 가지고 언급 XY다른 값이 존재하는 것, Z그런 것을 X+Z의지가 제대로 캐스트 경우를 , 동일 ). 부호없는 값이 단순히 저장 위치 유형이고 중간 표현식 유형이 아닌 경우 (예 : 가장 큰 정수 유형에 해당하는 부호없는 동등 물이없고 부호없는 유형에 대한 산술 연산이 큰 부호있는 유형으로 변환 된 것처럼 동작하는 경우) 정의 된 래핑 동작에는 그다지 필요하지 않지만, 가산 역수를 포함하지 않는 유형으로 계산하는 것은 어렵습니다.Y , Y-Z의지X

이 기능은 랩 어라운드 동작이 실제로 유용한 상황 (예 : TCP 시퀀스 번호 또는 해시 계산과 같은 특정 알고리즘)에 도움이됩니다. 또한 계산을 수행하고 오버플로 여부를 확인하는 것이 오버플로 여부를 미리 확인하는 것보다, 특히 계산에 사용 가능한 가장 큰 정수 유형이 포함 된 경우 오버플로를 감지해야하는 상황에서 도움이 될 수 있습니다.


나는 따르지 않습니다-왜 첨가물을 뒤집는 것이 도움이됩니까? 오버플로 동작이 실제로 유용한 상황을 실제로 생각할 수는 없습니다.
sleske

@sleske : 사람의 가독성을 위해 10 진수를 사용하는 경우, 에너지 미터가 0003을 읽고 이전 판독 값이 9995 인 경우 -9992 단위의 에너지가 사용되었거나 0008 단위의 에너지가 사용되었다는 의미입니까? 0003-9995 수율 0008을 가지면 후자의 결과를 쉽게 계산할 수 있습니다. 생산량을 -9992로하면 조금 더 어색해집니다. 그러나 그것을 할 수 없다면, 0003에서 9995를 비교하고, 더 작은 것을 알아 차리고, 역 빼기를하고, 9999에서 그 결과를 빼고, 1을 더해야합니다.
supercat

@sleske : 인간과 컴파일러 모두가 표현을 다시 작성하고 단순화하기 위해 산술의 연관성, 분배 성 및 계산법을 적용 할 수있는 것이 매우 유용합니다. 예를 들어, 발현이 경우 a+b-c루프 내 연산되지만 bc그 루프 내에 일정한 그것이의 연산 이동 도움이 될 수있는 (b-c)루프 밖에 있지만, 그렇게하는 것은 무엇보다도 필요 (b-c)에 가한 값을 산출 a, 을 산출 할 것이고 a+b-c, 이는 c부가적인 역수를 요구합니다 .
supercat

: 설명해 주셔서 감사합니다. 올바르게 이해하면 모든 예제에서 실제로 오버플로를 처리하려고한다고 가정합니다. 내가 만난 대부분의 경우 오버플로는 바람직하지 않으며 오버플로를 사용한 계산 결과가 유용하지 않기 때문에이를 방지하려고합니다. 예를 들어 에너지 미터의 경우 오버플로가 발생하지 않는 유형을 사용하려고 할 수 있습니다.
sleske

1
... 의 산술 값이 유형 내에서 표현 가능한지 여부 (a+b)-c와 동일 하므로 대체 가능한 값 범위에 관계없이 유효합니다 . a+(b-c)b-c(b-c)
supercat

1

부호없는 산술이 정의되는 또 다른 이유는 부호없는 숫자가 정수 모듈로 2 ^ n을 형성하기 때문입니다. 여기서 n은 부호없는 숫자의 너비입니다. 부호없는 숫자는 단순히 십진수 대신 이진수를 사용하여 표현 된 정수입니다. 모듈러스 시스템에서 표준 작업을 수행하는 것은 잘 알려져 있습니다.

OP의 인용문은이 사실을 언급하지만 부호없는 정수를 이진수로 표현하는 논리적이고 단 하나의 방법이 있다는 사실을 강조합니다. 반대로 부호있는 숫자는 대부분 2의 보수를 사용하여 표현되지만 표준 (6.2.6.2 절)에 설명 된대로 다른 선택이 가능합니다.

2의 보수 표현을 사용하면 특정 연산이 이진 형식으로 더 의미가 있습니다. 예를 들어, 음수를 증가시키는 것은 양수의 경우와 동일합니다 (오버플로 조건에서 예상 됨). 기계 수준의 일부 작업은 부호있는 숫자와 부호없는 숫자에 대해 동일 할 수 있습니다. 그러나 이러한 작업의 결과를 해석 할 때 긍정적이고 부정적인 오버플로와 같은 일부 경우에는 의미가 없습니다. 또한 오버플로 결과는 기본 서명 된 표현에 따라 다릅니다.


구조가 필드가 되려면 가산 성 ID 이외의 구조의 모든 요소에 곱하기 역수가 있어야합니다. 정수 합치 mod N의 구조는 N이 1이거나 소수 인 경우에만 필드가됩니다 [N == 1 인 경우 불량 필드]. 내 답변에서 놓친 느낌이 있습니까?
supercat

네 말이 맞아 나는 주요한 힘 계수에 혼란스러워했다. 원본 응답이 수정되었습니다.
yth

추가 여기 혼동하는 것은이 때문이다 입니다 위해 2 ^ N의 필드 정수는 ^ n은 2 모듈로에, 그냥하지 링 동형이다.
케빈 벤툴로

그리고 2 ^ 31-1은 Mersenne Prime입니다 (그러나 2 ^ 63-1은 소수가 아닙니다). 따라서 내 원래의 생각은 망가졌습니다. 또한 정수 크기는 당시와 달랐습니다. 그래서 제 생각은 최선의 개정 주의자였습니다.
yth

부호없는 정수가 필드 (필드가 아닌)를 형성하고 하위 부분을 취하면 링이 생성되고 전체 값에 대한 작업을 수행 한 다음 잘리는 것은 하위 부분에 대한 작업을 수행하는 것과 동일하게 작동합니다 .IMHO 거의 확실하게 고려합니다.
슈퍼 캣
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.