32 비트 컴퓨터에서-(-2147483648) =-2147483648 인 이유는 무엇입니까?
나는 질문이 자명하다고 생각합니다. 아마도 오버플로와 관련이 있다고 생각하지만 여전히 이해하지 못합니다. 내부적으로 무슨 일이 일어나고 있습니까?
왜 -(-2147483648) = -2147483648
(적어도 C로 컴파일하는 동안)?
(접미사가없는) 정수 상수 부정 :
이 표현 -(-2147483648)
은 C로 완벽하게 정의되어 있지만 왜 이런 식인지 분명하지 않을 수 있습니다.
를 쓰면 -2147483648
정수 상수에 적용된 단항 빼기 연산자로 구성됩니다. 2147483648
로 표현할 수없는 경우 int
s는 long
또는 long long
* (둘 중 먼저 맞는 것)로 표시됩니다. 여기서 후자는 C 표준에서 해당 값을 포함하도록 보장합니다 † .
이를 확인하기 위해 다음과 같이 조사 할 수 있습니다.
printf("%zu\n", sizeof(-2147483648));
8
내 컴퓨터에서 양보 합니다.
다음 단계는 두 번째 -
연산자 를 적용 하는 것입니다.이 경우 최종 값은입니다 2147483648L
(결국으로 표시되었다고 가정 long
). int
객체 에 할당하려고하면 다음과 같이됩니다.
int n = -(-2147483648);
실제 동작은 구현에 따라 정의됩니다 . 표준 참조 :
C11 §6.3.1.3 / 3 부호있는 정수와 부호없는 정수
그렇지 않으면 새 유형이 서명되고 값을 표시 할 수 없습니다. 결과가 구현 정의이거나 구현 정의 신호가 발생합니다.
가장 일반적인 방법은 단순히 상위 비트를 잘라내는 것입니다. 예를 들어 GCC는 이를 다음과 같이 문서화 합니다.
너비 N의 유형으로 변환하는 경우 값은 유형의 범위 내에 있도록 모듈로 2 ^ N으로 축소됩니다. 신호가 발생하지 않습니다.
개념적으로 너비 32 유형으로의 변환은 비트 AND 연산으로 설명 할 수 있습니다.
value & (2^32 - 1) // preserve 32 least significant bits
에 따르면, 2의 보수 산술의 값은 n
모두 0과 1의 값을 나타내고, MSB (부호) 비트 세트와 함께 형성되고, -2^31
즉, -2147483648
.
int
개체 부정 :
int
값을 갖는 객체 를 부정하려고 시도 -2147483648
하면 2의 보수 기계를 가정하면 프로그램은 정의되지 않은 동작 을 나타냅니다 .
n = -n; // UB if n == INT_MIN and INT_MAX == 2147483647
C11 §6.5 / 5 표현식
는 IF 예외 조건이 표현식 평가 동안 발생 (결과 수학적하지 그 유형 표현 가능한 값의 범위 또는 정의되지 않은 경우 즉,), 동작이 정의되지 않는다.
추가 참조 :
*) 철회 된 C90 Standard에는 long long
유형 이없고 규칙이 달랐습니다. 특히, 소수 unsuffixed이었다 시퀀스 int
, long int
, unsigned long int
(C90 §6.1.3.2 정수 상수).
†) 이는 LLONG_MAX
최소한 +9223372036854775807
(C11 §5.2.4.2.1 / 1) 이상이어야합니다 .
참고 :이 답변은 많은 컴파일러에서 여전히 사용되는 구식 ISO C90 표준에는 적용되지 않습니다.
우선 C99, C11에서이 표현 -(-2147483648) == -2147483648
은 사실 거짓입니다 .
int is_it_true = (-(-2147483648) == -2147483648);
printf("%d\n", is_it_true);
인쇄물
0
그렇다면 이것이 사실로 평가되는 것이 어떻게 가능합니까? 기계는 32 비트 2의 보수 정수를 사용하고 있습니다. 는 2147483648
따라서 하나가 될 것이다 매우 32 비트에 맞지 않는 정수 상수 long int
또는 long long int
그것을 어디에 적합한 어느에 따라 것은 처음이다. 이 부정은 결과가됩니다 -2147483648
. 숫자 -2147483648
가 32 비트 정수에 들어갈 수 있더라도 표현식 -2147483648
은 앞에 단항이있는> 32 비트 양의 정수로 구성됩니다 -
!
다음 프로그램을 시도 할 수 있습니다.
#include <stdio.h>
int main() {
printf("%zu\n", sizeof(2147483647));
printf("%zu\n", sizeof(2147483648));
printf("%zu\n", sizeof(-2147483648));
}
이러한 시스템의 출력은 아마도 4, 8 및 8 일 것입니다.
이제 -2147483648
부정하면 다시 결과가 나옵니다 +214783648
. 이는 여전히 long int
또는 유형 long long int
이며 모든 것이 정상입니다.
C99, C11에서 정수 상수 표현식 -(-2147483648)
은 모든 준수 구현에서 잘 정의됩니다.
이제이 값이 int
32 비트 및 2의 보수 표현을 사용하여 유형의 변수에 할당 될 때 값은 표현할 수 없습니다. 32 비트 2의 보수 값은 -2147483648에서 2147483647까지입니다.
C11 표준 6.3.1.3p3 에서는 정수 변환에 대해 다음과 같이 말합니다.
- [시기] 새 유형이 서명되고 값을 표시 할 수 없습니다. 결과가 구현 정의 이거나 구현 정의 신호가 발생합니다.
즉, C 표준은이 경우의 값이 무엇인지 실제로 정의하지 않거나 신호 발생으로 인해 프로그램 실행이 중지 될 가능성을 배제하지 않고 구현 (예 : 컴파일러)에 맡깁니다. ) 처리 방법 결정 (C11 3.4.1) :
구현 정의 동작
각 구현이 선택 방법을 문서화하는 지정되지 않은 동작
및 (3.19.1) :
구현 정의 값
각 구현에서 선택 방법을 문서화하는 지정되지 않은 값
In your case, the implementation-defined behaviour is that the value is the 32 lowest-order bits [*]. Due to the 2's complement, the (long) long int value 0x80000000
has the bit 31 set and all other bits cleared. In 32-bit two's complement integers the bit 31 is the sign bit - meaning that the number is negative; all value bits zeroed means that the value is the minimum representable number, i.e. INT_MIN
.
[*] GCC documents its implementation-defined behaviour in this case as follows:
The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (C90 6.2.1.2, C99 and C11 6.3.1.3).
For conversion to a type of width
N
, the value is reduced modulo2^N
to be within range of the type; no signal is raised.
This is not a C question, for on a C implementation featuring 32-bit two's complement representation for type int
, the effect of applying the unary negation operator to an int
having the value -2147483648
is undefined. That is, the C language specifically disavows designating the result of evaluating such an operation.
Consider more generally, however, how the unary -
operator is defined in two's complement arithmetic: the inverse of a positive number x is formed by flipping all the bits of its binary representation and adding 1
. This same definition serves as well for any negative number that has at least one bit other than its sign bit set.
Minor problems arise, however, for the two numbers that have no value bits set: 0, which has no bits set at all, and the number that has only its sign bit set (-2147483648 in 32-bit representation). When you flip all the bits of either of these, you end up with all value bits set. Therefore, when you subsequently add 1, the result overflows the value bits. If you imagine performing the addition as if the number were unsigned, treating the sign bit as a value bit, then you get
-2147483648 (decimal representation)
--> 0x80000000 (convert to hex)
--> 0x7fffffff (flip bits)
--> 0x80000000 (add one)
--> -2147483648 (convert to decimal)
Similar applies to inverting zero, but in that case the overflow upon adding 1 overflows the erstwhile sign bit, too. If the overflow is ignored, the resulting 32 low-order bits are all zero, hence -0 == 0.
I'm gonna use a 4-bit number, just to make maths simple, but the idea is the same.
In a 4-bit number, the possible values are between 0000 and 1111. That would be 0 to 15, but if you wanna represent negative numbers, the first bit is used to indicate the sign (0 for positive and 1 for negative).
So 1111 is not 15. As the first bit is 1, it's a negative number. To know its value, we use the two-complement method as already described in previous answers: "invert the bits and add 1":
- inverting the bits: 0000
- adding 1: 0001
0001 in binary is 1 in decimal, so 1111 is -1.
The two-complement method goes both ways, so if you use it with any number, it will give you the binary representation of that number with the inverted sign.
Now let's see 1000. The first bit is 1, so it's a negative number. Using the two-complement method:
- invert the bits : 0111
- add 1: 1000 (8 in decimal)
So 1000 is -8. If we do -(-8)
, in binary it means -(1000)
, which actually means using the two-complement method in 1000. As we saw above, the result is also 1000. So, in a 4-bit number, -(-8)
is equals -8.
In a 32-bit number, -2147483648
in binary is 1000..(31 zeroes)
, but if you use the two-complement method, you'll end up with the same value (the result is the same number).
That's why in 32-bit number -(-2147483648)
is equals -2147483648
It depends on the version of C, the specifics of the implementation and whether we are talking about variables or literals values.
The first thing to understand is that there are no negative integer literals in C "-2147483648" is a unary minus operation followed by a positive integer literal.
Lets assume that we are running on a typical 32-bit platform where int and long are both 32 bits and long long is 64 bits and consider the expression.
(-(-2147483648) == -2147483648 )
The compiler needs to find a type that can hold 2147483648, on a comforming C99 compiler it will use type "long long" but a C90 compiler can use type "unsigned long".
If the compiler uses type long long then nothing overflows and the comparision is false. If the compiler uses unsigned long then the unsigned wraparound rules come into play and the comparision is true.
For the same reason that winding a tape deck counter 500 steps forward from 000 (through 001 002 003 ...) will show 500, and winding it backward 500 steps backward from 000 (through 999 998 997 ...) will also show 500.
This is two's complement notation. Of course, since 2's complement sign convention is to consider the topmost bit the sign bit, the result overflows the representable range, just like 2000000000+2000000000 overflows the representable range.
As a result, the processor's "overflow" bit will be set (seeing this requires access to the machine's arithmetic flags, generally not the case in most programming languages outside of assembler). This is the only value which will set the "overflow" bit when negating a 2's complement number: any other value's negation lies in the range representable by 2's complement.
참고URL : https://stackoverflow.com/questions/42462352/why-is-2147483648-2147483648-in-a-32-bit-machine
'program tip' 카테고리의 다른 글
Angular2 : 사용자 지정 파이프를 찾을 수 없습니다. (0) | 2020.11.28 |
---|---|
Linux에서 문자열 리터럴의 메모리 주소가 다른 것과 다른 이유는 무엇입니까? (0) | 2020.11.28 |
tf.layers.conv2d 및 tf.layers.dense의 기본 커널 이니셜 라이저는 무엇입니까? (0) | 2020.11.28 |
식 트리 람다는 null 전파 연산자를 포함 할 수 없습니다. (0) | 2020.11.28 |
SVN / TortoiseSVN 고통스럽게 느림 (0) | 2020.11.28 |