2016-07-14 9 views
2

부호있는 정수는 부호 비트가 -(2^N) 인 2의 보수를 통해 x86에 표현됩니다. 그 결과 대표적인 값 범위는 -2^N2^N - 1 사이입니다 (예 : -32768에서 32767까지).INT_MIN * -1은 x86에서 작동하지 않습니까?

내 시스템에서 최소 부호있는 정수 값을 가져 와서 -1으로 곱하면 시스템에서 부호있는 정수의 최대 표현 가능 값보다 큰 최대 값을 "강제 설정"하려고 할 때 궁금한 점이있었습니다.

#include <stdio.h> 
#include <limits.h> 

int main(void){ 
    signed int x, y; 

    x = INT_MIN; 
    y = x * -1; 

    printf("%d\n%d\n", x, y); 

    return 0; 
} 

이 다음과 같은 출력 결과 :

# gcc -std=c89 -pedantic int_min_test.c 

# ./a.out 
-2147483648 
-2147483648 

내가 (전형적인 값 롤오버 결과) 정수 오버 플로우를 기대했지만, 아무런 조작 x의 곱셈에 대한 일어났다없는 것처럼 나타납니다 -1.

INT_MIN-1의 곱셈에 x86을 사용합니까?

+4

롤오버가 가능한 가장 작은 값을 다시 생성하지 않습니까? –

+0

정의되지 않은 동작 (2의 보수) - x86과 관련이 없습니다. –

+0

@KerrekSB가 말한 바. UB없이 이것을 관찰하고 싶다면 부호없는 타입을 사용하십시오. –

답변

4

에 볼 것으로 예상한다 무엇을 함께 할 수 없다, 라인 y = x * -1;는 다음과 같은 명령을 사용하여 계산된다 단어를 추가 한 다음 1을 더합니다. 32 비트 2의 보수는 다음과 같습니다.

0x80000000 # Start value 
0x7FFFFFFF # Flip bits 
0x80000000 # Add 1 

컴퓨터에서 알 수 있듯이 컴퓨터는 사용자가하고있는 것과 정확히 일치합니다. 이것은 negAF, CF, OF, PF, SFZF 플래그를 수정하므로 아무 작업도 아닙니다. 이것은 사용 된 2 진 표현의 아티팩트 일뿐입니다. 다른 사람들이 말했듯이 그것은 단순히 정의되지 않은 행동입니다.

4

C의 경우 (질문의 원래 태그 지정과 C로 작성된 예제 코드를 기반으로 함) 정의되지 않은 동작이므로 C 표현식 INT_MIN * -1의 결과는 "a no-op"가 아니며 정의되지 않은 동작입니다. . 실제로는 결과를 관찰하는 방법에 따라 일관성없이 결과가 일관 적으로 일치하지 않을 수도 있습니다. 하지 마. 당신이 86 imul 명령에 대해 물어보고 싶은 경우

, 즉 다른 질문 나는 0xffffffff로 (32 비트) 0x80000000imul0x80000000을 생산 믿습니다.

neg %eax 

NEG 작업의 비트를 뒤집 :하지만 당신은 GCC 4.8.5을 사용하여 C.

+0

예, 저는 x86 opcode 동작에 관심이 있습니다. 혼란을 피하기 위해'C' 태그를 제거했습니다. –

+2

질문에있는 코드가 C 일 때만 태그를 변경하면 도움이되지 않습니다. asm에 관해 질문하고 싶다면 asm 샘플 코드를 사용하십시오. C는 직접 asm으로 변환하지 않습니다. –

+0

그것은 좋은 지적입니다. C 구현은 정의되지 않은 동작으로 opcode 시퀀스를 생성 할 수 있습니다. 나는 x86 문제에 초점을 맞추기 위해 더 좋은 질문을 다시 쓸 것이다. –

2

x86 프로세서에서 "int"에 -1을 곱하는 지침은 0x80000000 비트 패턴이 주어지면 비트 패턴이 결과로 동일한 비트 패턴을 생성합니다. 어떤 컴파일러에

  1. , 수학의 기본을 사용하여 수행되는 것처럼 모든 부호있는 정수 연산이 동작 : 그것은주의하는 것이 중요하다, 그러나, x86 용하는 C 컴파일러는 카테고리의 번호로 를 나눌 수 있습니다 정확한 길이의 연산은 무한 정밀도로 연산을 수행하고 정수 모듈을 여러 범위로 가져 오는 데 필요한 여러 범위 값을 더하거나 빼는 것과 같습니다.이러한 동작은 범위가 다르다는 것을 제외하고는 부호없는 형식에서 발생하는 것과 유사합니다. 그러한 컴파일러에서는 -INT_MIN == INT_MIN입니다.

  2. 일부 컴파일러에서 모든 부호있는 정수 연산은 무한 정밀도로 수행되는 것처럼 동작하지만 정수 범위의 임의 배수를 범위를 벗어난 값에 더하거나 뺄 수 있습니다. 형식의 범위를 벗어나는 숫자처럼 동작합니다. 이렇게하면 "x + 1> y"와 같은 표현식을 "x> = y"로 바꿀 수 있으며 오버 플로우가 일관되게 줄 바꿈되어야하는 경우 불가능한 방식으로 코드가 루프 외부로 이동할 수 있습니다. 그러한 컴파일러에서 -INT_MININT_MIN과 같을 수도 있고 -(long)INT_MIN과 같을 수도 있고, 하위 32 비트가 INT_MIN과 일치하는 다른 값일 수도 있습니다. 그러한 컴파일러 중 일부는 변수에 값을 저장하는 경우 범위를 벗어난 값을 자르지 만 그렇지 않은 변수는 일부만 잘라낼 수 있습니다. 이러한 컴파일러의 핵심 기능은 오버플로가 발생하는 경우 결과의 '추가'비트가 관련되지 않으면 코드가 오버플로를 방지 할 필요가 없다는 것입니다. uint1=ushort1*ushort2;].

  3. 일부 컴파일러는 시간과 인과 관계의 법칙을 부정하기위한 기초로 정수 오버플로를 사용합니다. 이러한 컴파일러를 사용할 때, 생성 된 기계 코드가 프로그램의 회의 요구 사항에 기여하는 것과 관계없이 모든 비용으로 오버 플로우를 방지하는 논리를 포함해야합니다.

개인적으로 두 번째 형식의 의미가 가장 적합하다고 생각합니다. 은 세 번째에서 사용할 수있는 거의 모든 유용한 최적화를 허용하고 프로그래머가 세 번째 코드에서는 가능하지 않은 최적화 을 가능하게합니다. 두 번째 형태의 행동 보증이 오버 플로우가 발생하더라도 프로그램이 행동 요구 사항을 충족시키는 데 충분하면 프로그래머가 오버플로를 처리하도록 강요하면 어쨌든 코드의 효율성이 떨어집니다. 오버플로 검사가 효율성 저하의 비용으로도 좋은 아이디어 인 경우가 종종 있지만 필자는 프로그래머가 필요하지 않은 것보다 효율적이지 않은 코드를 작성하도록 컴파일러를 "최적화"하는 것에 대해 부조리가 있다고 생각합니다.