Fundamental of CS/: : CSAPP

[CSAPP] Ch 2. 정보의 표현과 처리 : (2) 정수의 표시

Jay.P Morgan 2023. 12. 14. 12:44

 

  2.0 Intro

 

  이 절에선 정수를 인코딩하기 위해 사용할 수 있는 두 가지 방법 (양수만 표시할 수 있는 방법과, 음수, 0, 양수 모두를 표시할 수 있는 방법) 에 대해 설명한다. 나중에 이들이 수학적 특성, Low-Level 수준을 볼 때 매우 연관되어있음을 알게 될 것이다. 그리고 인코딩된 정수를 다른 길이의 표현에 맞도록 조절하기 위해 확장하거나 축소하는 효과에 대해서도 살펴본다.

※ 정수 데이터와 산술연산을 위한 용어. 아래 첨자 w는 데이터 표시에 필요한 비트수 [단순 참고용]

 

 

  2.1 정수형 데이터 타입

 

  2.1.1 C의 다양한 정수형 데이터 타입

 

  아래 두 그림에는 전형적인 32bit와 64bit 프로그램들에서 이들이 갖는 값의 범위를 나타내었다. 각 타입은 unsigned로 선언되어 표시된 숫자가 모두 양수인지, 아니면 기본타입으로 음수도 나타낼 수 있는지 뿐만 아니라 키워드 char, short, long을 통해 그 크기까지 명시한다.

※ 32bit 프로그램에서의 C 정수형 자료형의 일반적인 범위
※ 64bit 프로그램에서의 C 정수형 자료형의 일반적인 범위

 

  위 두 그림에서 주목해야할 특징은, 범위들이 대칭이 아니라는 것이다. (음수의 범위가 1 더 넓다)

 

  C 표준에서는 각 데이터 타입에서 나타낼 수 있어야 하는 최소한의 범위를 정의한다. 특히, 고정길이 자료형들을 제외하고, 이들은 양과 음의 숫자들이 대칭적인 범위만을 갖도록 요구하고 있다.

 

  그리고, 데이터 타입 int는 비록 16비트 머신 시절로 돌아갈지라도 2바이트 숫자들로 구현될 수 있었다는 것을 알 수 있다. long 크기도 다른 경우와 마찬가지로 4바이트로 구현될 수 있으며, 일반적으로 32bit 프로그램에서 그렇다.

 

  ※ C와  C++은 부호형(default)과 비부형을 지원하는 반면, Java는 부호형만 지원한다.

 

 

  2.2 비부호형의 인코딩

 

  2.2.1 비부호형 인코딩의 정의

 

  인코딩에서 x의 각 비트들은 0이나 1의 값으로 갖는다. 1을 갖는 경우 2^i 값이 숫자의 값을 계산할 때 포함되어야 한다는 것을 나타낸다. 이런 해석과정을 함수 B2U로 나타낸다. [binary-to-unsisgned]

※ 비트 벡터와 정수의 매핑
※ w = 4인 비부호형 예제

 

 

  2.2.2 비부호형 인코딩의 유일성

 

  함수 B2U는 전단사(bijection) 특성을 갖는다.

  전단사 특성의 수학적 의미는 어떤 함수 f 가 양방향으로 사상할 수 있다는 것을 말한다. 어느 값 x를 어떤 값 y로 매핑하며, y=f(x)이고, 그 역방향으로도 성립해 모든 값 y에 대해 f(x)=y인 유일한 값 x가 존재한다. 이는 역함수 f^-1에 의해 얻을 수 있으며, x = f^-1 (y)로 표시한다.

 

 

  2.3  2의 보수(two's complement) 인코딩

 

  많은 응용에서 음수 값을 표시하는 경우들이 있다. 부호형 숫자를 컴퓨터는 일반적으로 2의 보수 형식으로 나타낸다. 이는 워드의 가장 중요한 비트를 음수 자리값을 갖는 것으로 해석하는 형태로 정의된다.

 

  2.3.1 2의 보수 인코딩의 정의

 

  벡터 x = [x_w-1 , x_w-2 , . . . , x_0 ] 에 대해

 

  가장 중요한 비트인 x_w-1 은 부호비트이다.

 

※ w = 4인 경우, 2의 보수 예제

 

 

  2.3.2 2의 보수 인코딩의 유일성

 

  비부호형 표시에서 본 것과 같이 표시 가능한 범위의 모든 숫자는 w비트 2의 보수 숫자로 유일하게 인코딩된다. 즉, 함수 B2T는 전단사 함수이다.

 

 

  2.3.2 2의 보수 특징

 

 2의 보수의 범위는 비대칭적이다.

  |Tmin| = |Tmax| + 1이므로, Tmin에 대응하는 양수 값이 없다. 이러한 비대칭성은 비트 패턴의 절반(부호비트가 1)이 음수를 표시하고, 나머지 절반(부호비트 0)이 비음수를 표시하기 때문이다.

  이로 인해 2의 보수 산술연산에서 기이한 특성들을 갖게되며, 난해한 프로그램 오류의 원인이 될 수 있다.

 

 비부호형 최대값은 2의 보수 최대값의 두 배 보다 1 크다

 

 

C 표준과는 달리, 거의 모든 컴퓨터에서 부호형 정수를 2의 보수 형식으로 나타낼 것을 요구하고 있다.

 

※ C 정수 자료형들의 보장된 범위

범위는 광범위한 머신과 컴파일러에서 호환성이 있으며, 프로그래머는 넘어서는 특정 범위를 가정하면 안된다.

 

 

Java 표준은 정수 데이터 타입의 범위와 표시를 상당히 구체적으로 하고있다. 자바에서 단일 바이트 데이터 타입은 char 대신 byte라고 부른다.

 

 

 

  2.4 비부호형과 부호형 간의 변환

 

  C언어는 서로 다른 숫자 데이터 타입들간에 캐스팅을 허용한다.

	short		int	v = -12345;
	unsigned	short	uv = (unsigned short) v;
	printf("v = %d, uv = %d\n", v, uv);
───────────────────────────────────────────────────────────    
v = -12345, uv = 53191

 

  캐스팅의 효과는 비트의 값들은 동일하게 유지하지만, 이들 비트를 해석하는 방법은 변경한다는 것에 유의하자.

	unsigned	u = 4294967295u;	/* UMax */
	int	tu = (int) u;
	printf("u = %u, tu = %d\n", u, tu);
──────────────────────────────────────────────────────────────
u = 4294967295, tu = -1

 

  32bit 워드 크기에 대해 비부호형인 4,294,967,295(UMax32)를 표시하는 비트패턴과, -1의 2의 보수 표시가 동일하다는 것을 알 수 있다. unsigned를 int로 캐스팅하면 하부의 비트 표시는 그대로 남아있는다.

  숫자 값은 변할 수 있지만 비트 패턴은 변하지 않는다. 이것은 C언어에서 동일한 길이를 갖는 부호형과 비부호형 숫자 간의 변환을 다루는 일반적인 방법이다.

 

※ 12,345,  -12,345의 2이 보수 표시와 53,191의 비부호형 표시. 마지막 두 수는 동일한 비트표시를 갖는다는 점 유의.

 

 

  2.5 C에서 부호형과 비부호형의 비교

 

  C는 모든 정수 데이터 타입에 대해 부호형과 비부호형 산술연산을 지원하고, 비부호형과 부호형 간의 변환을 허용한다.

  그리고, 대부분의 시스템은 기본 비트 표시는 바뀌지 않는다는 규칙을 따른다. (2의 보수)

 

  ※ 12345u, 0x1A2Bu와 같이 상수를 선언할 때 문자 "U"나 "u"를 접미어로 추가하면 비부호형 상수를 만들게 된다.

 

  2.5.1 부호형 ↔ 비부호형 간의 변환

 

 명시적 캐스팅에 의한 변환

 

 다른 타입 변수에 할당될 때 묵시적으로 발생하는 변환

 

 

  2.5.2 부호형과 비부호형의 비교

 

  숫자 값을 printf로 출력할 때 디렉티브 %d, %u, %x는 수를 부호형 십진수, 비부호형 십진수, 16진수 형식으로 출력하기 위해 사용한다. (printf는 타입 정보를 활용하지 않는다는 점 유의)

 

 

  한 개의 오퍼랜드가 부호형이고, 다른 것이 비부호형인 경우의 연산에서 C는 묵시적으로 부호형 인자를 비부호형으로 변환하고, 숫자들이 비음수라고 가정하고 계산을 수행한다.

※ C 변환규칙

 

 

 

  2.6 수의 비트 표시를 확장하기

 

  값은 동일하게 유지한 채 다른 길이의 워드로 정수를 변환하는 것은 일반적인 연산의 하나이다.

  대상 데이터 타입이 원하는 값을 나타내기에 너무 작으면 불가능할 수 있다. 그러나, 보다 작은 길이에서 더 큰 길이의 자료형으로 변환하는 것은 언제나 가능해야 한다.

 

  2의 보수 표시를 하는 빅 엔디안 머신을 살펴보자.

 

-12345의 2의 보수 표시와 53191의 비부호형 표시가 16비트 워드 길이에 대해서는 동일할지라도 32비트 워드 길이에서는 다르다는 것을 알 수 있다.

 

  가장 중요한 비트 1번을 16번 복사해서, 맨 앞에 16진수 0xFFFF 표시를 덧붙였다. 후자의 경우는 16진수 0x0000으로 나타내는 16개의 0을 앞에 붙였다.

 

 

 

  2.7 숫자의 절삭

 

  이번에는 나타내는 비트의 개수를 줄이는 경우를 살펴보자.

    x를 short로 타입 변환(casting) 때 32bit int를 16bit short로 절삭한다. 이 16bit 패턴은 -12,345를 2의 보수로 표시한 것이다. 이것을 다시 int로 타입 변환할 때 부호 비트는 상위 16bit를 1로 세팅해서 -12,345의 32bit 2의 보수 표시를 만들게 된다.

※ 맨 아래 BIN의 비트를 살펴보자

 

※ BIN값을 살펴보면, 53,191에 2의 보수를 취한 값임을 알 수 있다.

 

※ 16bit에서 64bit 자료형으로 나타낼 시, 상위 bit는 1로 세팅하여 -12,345를 만든다.

 

 

  2.7.1  비부호형 수의 절삭

  이 법칙은, 삭제되는 모든 비트들은 i>=k인 2^i의 자리값을 가지며, 따라서 이 자리값들은 모듈 계산으로 모두 0이 된다는 것이다.

  위의 식을 전개하면 아래와 같다.

 

  2.7.2  2의 보수 숫자의 절삭

  x mod 2^k는 0과 2^k - 1 사이의 수를 갖는다.  여기에 함수 U2Tk를 적용하면 가장 중요한 비트인 x_(k-1) 이 자리값 2^(k-1) 대신 - 2^(k-1)을 갖는 효과를 낸다.

 

x = 53,191을 int에서 short로 변환하는 예제를 보자.  이 수를 16bit 2의 보수로 변환하면 x=53191 - 65535 = -12345가 된다.

 

 

 

  2.8 Signed와 Unsigned에 관한 조언

 

  부호형을 비부호형으로 묵시적인 형변환을 하면 다소 예외의 동작을 보인다. 이로 인해 종종 버그가 발생하기도 한데, 알아내기 상당히 어려울 수 있다. 형변환이 코드 내에서 명확한 표시 없이 발생하기 때문이다.

 

  실제로 C 이외의 다른 언어들은 unsigned 정수를 지원하지 않는다. 그러나 비부호형 값들은 워드 길이 데이터를 단지 비트들의 집합으로 생각하려는 경우 매우 유용하다. 주소는 원래 unsigned여서 시스템 프로그래머들은 unsigned 타입이 유용하다고 생각한다.