컴퓨터에서의 수 표현

(유동소수점에서 넘어옴)

1 개요

컴퓨터10진수가 아닌 2진수로 수를 표현한다. 이 문서는 일반적인 32bit 컴퓨터[1]정수실수를 어떻게 표현하는지를 정리한 문서이다.

2 컴퓨터에서 정수 표현하기

일반적으로 컴퓨터에서 사용되는 정수형의 종류는 다음과 같다.

크기
char8 bit
short16 bit
int32 bit
long32 bit

각 정수 형은 음수를 표현할 수 없고 양수 크기가 두배로 지원되는 unsigned 형을 가진다.
위 표의 크기는 32비트 윈도우를 기준으로 한 것이다. 운영체제, CPU 아키텍처, 프로그래밍 언어에 따라 크기나 형의 이름이 다를 수 있다.[2]

정수 형의 크기가 중요한 프로그램을 개발 한다면 C의 std::int32_t와 같이 각 언어나 프레임워크에서 제공되는 크기가 명시적으로 표현된 자료형을 사용하도록 하자.

2.1 메모리에 저장하는 방법

메모리에서는 정수 데이터를 저장하기 위해 4칸을 쓰게 된다.[3][4] 4칸의 메모리에 정수를 어떤 순서로 저장하냐에 따라 Big Endian, Little Endian, Bi Endian으로 분류할 수 있다.

 <용어 정리>
 * MSB (Most Significant Byte) : '가장 중요한 바이트'라는 뜻으로, 가장 큰 자릿수를 담당하는 바이트를 지칭하는 말이다. 예를 들어, 0x12345678에서는 12를 담고 있는 바이트가 MSB이다.
 * LSB (Least Significant Byte) : '가장 중요하지 않은 바이트'라는 뜻으로, 가장 작은 자릿수를 담당하는 바이트를 지칭하는 말이다. 예를 들어, 0x12345678에서는 78을 담고 있는 바이트가 LSB이다.

2.1.1 Big Endian

MSB가 가장 앞쪽에 오는 저장방법. 예를 들어, 메모리에 0x12345678을 저장한다고 하면,

12345678
←작은 주소큰 주소 →

이런 식으로 저장하는 것이다. [5] 보통 IBM이나 썬마이크로시스템즈의 컴퓨터들이 이 방식을 채택한다.
JavaJVM은 호스트 운영체제나 하드웨어와 상관 없이 항상 Big Endian을 사용한다.

2.1.2 Little Endian

LSB가 가장 앞쪽에 오는 저장방법. 예를 들어, 메모리에 0x12345678을 저장한다고 하면,

78563412
←작은 주소큰 주소 →

이런 식으로 저장하는 것이다. 보통 인텔사의 칩셋과 호환이 되는 컴퓨터들이 이 방식을 채택한다.

2.1.3 Bi Endian

컴퓨터 시스템이, 또는 경우에 따라 사용자가 직접 데이터 저장 방식을 Big Endian과 Little Endian 중에서 고를 수 있는 형태.

자신의 컴퓨터가 어느 저장 방법을 따르는지를 알고 싶으면 간단한 c코드를 통해 이를 체크할 수 있다. [6]

/*Endian_chk.c
*
*이진수 0000 0000 / 0000 0000 / 0000 0000 / 0000 0001,,(2),, 을 i에 저장한 후, 정수형 변수 i를 문자형 변수로 캐스팅한다.
*그렇게 하면 4바이트의 i는 1비트의 길이 4인 배열이 된다.
*이제 이 배열의 시작 주소를 읽는다.
*이 값이 1이면 가장 마지막 자리가 가장 작은 주소에 저장되어 있다는 것(0000 0001,,(2),,)이므로 Little Endian이다.
*이 값이 0이면 가장 큰 자리가 가장 작은 주소에 저장되어 있다는 것(1000 0000,,(2),,)이므로 Big Endian이다.
*/
#include 
int main()
{
int i = 0x00000001;
if( ((char *)&i)[0] )
{
printf( "Littile Endian\n" );
} else
{
printf( "Big Endian\n" );
}
} 

2.2 표현법

2.2.1 음수를 표현하는 경우 (signed int)

총 32개의 비트 중 첫 번째 비트를 부호 표현을 위해 따로 배정한다. 이를 부호 비트(signed bit)라 부른다. 부호 비트가 0이면 양수를, 1이면 음수를 나타낸다.

2.2.1.1 양수를 표현할 때

부호 비트는 0으로 놓고, 남은 숫자로 2진수를 그대로 표현하면 된다. 즉,
0100 0000 0000 0000 0000 0000 0000 0000(2) (0x40000000) = 1073741824
0000 0100 1001 1000 0000 0000 0011 1111(2) (0x0498003F) = 77070399
0000 0000 0000 0000 0000 0000 0000 1000(2) (0x00000008) = 8
이런 식이다.참 쉽죠?
따라서 표현할 수 있는 가장 큰 32비트 값은 0111 1111 1111 1111 1111 1111 1111 1111(2) (0x7FFFFFFF) = 2147483647 가 된다.

2.2.1.2 음수를 표현할 때

논리 회로가 음수를 표현하는 방법은 여러 가지가 있다. 대표적으로 3가지를 꼽자면 '부호 절대값 방법(Sign Magnitude)', '1의 보수 방법(1's Complement)', '2의 보수 방법(2's Complement)'가 있다.

  • 부호 절대값 방법(Sign Magnitude) : 부호 비트를 제외한 수를 양수값으로 읽고, 마이너스를 붙이는 방법. 즉 이진수 000011(2) = +3으로, 100011(2) = -3으로 인식하는 것이다. 이는 인간 입장에서 표기하기 직관적이란 장점이 있으나, 이진수 계산이 안되기에 컴퓨터 시스템에선 사용할 수 없다. [7]
  • 1의 보수 방법(1's Complement) : 양수값의 비트들을 반전시켜서 음수를 표현하는 방법.[8] 즉 이진수 000011(2) = +3의 비트를 모두 반전시켜 111100(2)을 만들어 -3을 표현하는 방법이다. 1의 보수 방법에서는 2진수 연산값이 실제 값과 같다.[9] 하지만 0을 나타내는 값이 000...00(2)(모든 비트가 0인 수)[10]과 111...11(2)(모든 비트가 1인 수)[11] 두 가지가 나오기에 컴퓨터 시스템에선 사용하지 않는다.
  • 2의 보수 방법(2's Complement) : 1의 보수 방법에 1을 더하는 방법.[12][13] 즉 이진수 000011(2) = +3의 비트를 모두 반전시켜 111100(2)을 만들고, 여기에 1을 더해 111101(2)로 -3을 표현하는 방법이다. 2의 보수 방법에서는 1의 보수 방법에서와 다르게 111...11(2)이 의미하는 값이 -1을 의미하므로 000...00(2) = 0과 구분된다.

컴퓨터는 이들 중 2의 보수 방법으로 음수를 표현한다.

signed int의 범위
표현할 수 있는 수의 최솟값 : 1000 0000 0000 0000 0000 0000 0000 0000(2) (0x80000000) = -2147483648[14]
표현할 수 있는 수의 최댓값 : 0111 1111 1111 1111 1111 1111 1111 1111(2) (0x7FFFFFFF) = 2147483647[15]

2.2.2 음수를 표현하지 않는 경우(unsigned int)

컴퓨터에서는 정수를 표현할 때 경우에 따라서는 음수를 표현하지 않아도 될 때가 있다. 이 때는 unsigned 선언을 해 주면 음수를 표현하지 않는 정수형(unsigned int)를 쓸 수 있다. 이 경우 부호 비트까지도 값을 나타내는데 쓰기에 표현할 수 있는 최대 정수 크기가 커진다. 물론 표현할 수 있는 가장 작은 정수가 커졌기에 int가 표현할 수 있는 범위가 늘어나는건 아니다.

unsigned int의 범위
표현할 수 있는 수의 최솟값 : 0000 0000 0000 0000 0000 0000 0000 0000(2) (0x00000000) = 0
표현할 수 있는 수의 최댓값 : 1111 1111 1111 1111 1111 1111 1111 1111(2) (0xFFFFFFFF) = 4294967295[16]

참고로, w비트의 정수가 표현할 수 있는 수의 범위는 다음과 같다.

타입범위
unsigned int0 ~ (2w - 1)
Signed - 부호 절대값 방법-(2w - 1 - 1) ~ (2w - 1 - 1)
Signed - 1의 보수 방법-(2w - 1 - 1) ~ (2w - 1 - 1)
Signed - 2의 보수 방법(signed int)-2w - 1 ~ (2w - 1 - 1)

2.2.3 형 변환(casting)

형 변환을 할 때의 규칙은 다음과 같다.

  • 각 비트의 숫자들은 그대로 유지한다.
  • 각 비트를 해석(interpret)하는 방법을 다르게 한다.

다시말해 signed int로 표현된 -3 = 111101(2)을 unsigned int로 해석해 61로 읽는 것이다.
형 변환은 명시적(explicit)으로 일으킬 수도 있지만, 묵시적(implicit)으로도 일어날 수 있다. 따라서 함부로 unsigned int를 선언하는 것은 위험할 수 있다. 대부분의 시스템에서 기본적인 정수형은 signed int이므로 데이터가 signed int로 해석될 수도 있기 때문이다. 따라서 unsigned int를 쓰는 상황은 단순히 음수 값을 가질 수 없는 상황에서보단 flag의 용도(계산을 하지 않는 용도)로 사용하는 것이 조금 더 알맞다 할 수 있겠다.

2.3 정수의 연산

컴퓨터 시스템에선 정수를 이용해 많은 연산을 수행한다. 여기선 컴퓨터가 정수를 가지고 연산을 할 때 일어나는 일들을 정리해 보았다. 이때, 이 컴퓨터는 w비트로 정수를 표현한다고 가정하자.

2.3.1 Unsigned의 덧셈

두 unsigned int x, y를 더할 때, x + y가 가질 수 있는 범위는
0 ≤ x + y ≤ 2w + 1 - 2
이고, 이 범위는 w비트로 표현할 수 없다.[17] 이때, 컴퓨터는 가장 아래 쪽 w비트만을 출력한다. 즉, 값 올림을 그대로 무시한다(버린다, truncate).[18]
예를 들어, w = 4인 시스템에서 9 + 12를 계산한다면
9 + 12 = 1001(2) + 1100(2) = 10101(2)
이 나온다. 여기서 밑줄 친 1은 값 올림이 일어난 부분을 의미한다. 이때 컴퓨터는 이 1을 무시하여 계산 결과를 0101(2)로 인식한다. 따라서 9 + 12 = 0101(2) = 5로 계산된다.

2.3.2 signed의 덧셈

두 signed int x, y를 더할 때, x + y가 가질 수 있는 범위는
-2w ≤ x + y ≤ 2w - 2
이고, 이 범위는 w비트로 표현할 수 없다. 이 때, unsigned의 덧셈과 마찬가지로 컴퓨터는 아래 쪽 w비트만을 출력한다.[19]
예) w = 4일 때,
(-8) + (-5) = 1000(2) + 1011(2) (= 10111(2)) = 0011(2) = 3 [20]
5 + 5 = 0101(2) + 0101(2) = 1010(2) = -6 [21]

2.3.3 signed의 음수화(negation)

signed int x의 범위는
-2w -1 ≤ x ≤ 2w -1 - 1
이므로 -x의 범위는
-(2w -1 - 1) ≤ -x ≤ 2w -1
이다. 이 때, x = -2w -1이면 -x를 w비트의 수로 표현할 수 없다는 것을 관찰할 수 있다. 이 경우 컴퓨터는 -x = -2w -1로 처리한다.
예) w = 4일 때,

x-x
-4 (1100(2))4 (0100(2))
-8 (1000(2))-8 (1000(2))
5 (0101(2))-5 (1011(2))
7 (0111(2))-7 (1001(2))

2.3.4 unsigned, signed의 곱셈

위와 마찬가지로 오버플로우에 대해 아래쪽 w비트의 결과만을 인식한다.
예) w = 3

xyx * y (원래 나와야 할 값)x * y (truncated)
Unsigned5 (101(2))3 (011(2))15 (1111(2))7 (111(2))
Signed-3 (101(2))3 (011(2))-9 (110111(2))-1 (111(2))

곱셈에 관해서는 조금 흥미로운 이야깃거리가 있다. 만약 곱하는 두 수 x, y 중 하나가 상수라면 대부분의 컴파일러에서는 곱셈 연산이 아닌 비트 이동(bit shift), 덧셈 등의 연산을 수행하는 것으로 최적화를 지원한다.[22] 이는 곱셈이 덧셈이나 비트 이동에 비해서 더 많은 자원을 사용하기 때문에 그렇다.
예를 들어, x * 14를 계산해야 한다면, 컴파일러는 이를 (x << 3) + (x << 2) + (x << 1)[23] 또는 (x << 4) - (x << 1)[24]으로 최적화한다.

3 컴퓨터에서 실수 표현하기

한 가지 명확히 해두어야 할 것은 정수와는 다르게 실수는 컴퓨터 상에서 완벽하게 표현할 수 없다는 것이다. 정수는 자릿수가 허용하는 범위에서의 모든 수를 완벽히 나타낼 수 있지만, 실수는 그 특성상 수를 완벽하게 나타낼 수 없고 (대부분의 경우) 오차가 생기게 된다.

컴퓨터에서 실수를 표현하는 방식으로 '고정 소수점 방식(Fixed Point System)'과 '부동 소수점 방식(Floating Point System)' 두 가지를 생각해볼 수 있다.

  • 고정 소수점 방식: 특정 위치에 소수점을 고정해 놓고 그 앞자리에는 실수의 정수부를, 뒷자리에는 실수의 소수부를 나타내는 방식이다. 예를 들어, 8비트로 실수를 나타내는 컴퓨터가 있다고 할 때 우리는 사전에 '8비트의 앞 4비트는 정수부를, 뒤 4비트는 소수부를 나타낸다'라는 약속을 해 놓는다. 그러면 1010.0111(2)을 '1010 0111'로, 11.011(2)은 '0011 0110'으로 나타낼 수 있다. 사실상 int와 다를 게 없으므로 구현하기는 매우 편하지만 이 방식으로는 표현할 수 있는 수의 범위가 아주 작다. 정밀도를 높이면 표현할 수 있는 수의 크기가 작아지고, 표현할 수 있는 수의 크기를 늘리면 정밀도가 낮아지며, 둘 모두를 높이면 저장해야 하는 비트 수가 비약적으로 늘어난다(...). 따라서 정밀할 필요는 없지만 단순해야 하는 시스템을 구성하거나 학부생들이 설계 실습할 때나 쓸까, 본격적으로 소수를 다루는 시스템에서는 전혀 쓰지 않는다.
  • 부동 소수점 방식: 고정 소수점 방식과는 다르게 소수점이 말 그대로 '떠다니면서' 실수를 표현하는 방식.[25] 주어진 실수를 x * 2y꼴로 표현한 후, x, y를 저장하는 방식으로 수를 저장한다. 상용로그에서 지표와 가수를 쓰는 것과 같은 원리고, 자연과학에서도 여기 특정한 규약을 더해 측정 정밀도도 함께 표현하는 거듭제곱꼴 표현을 사용한다. 부동소수점 방식은 굉장히 넓은 범위의 숫자를 표현할 수 있으면서도 (상대적으로) 높은 정밀성을 보장한다.[26] 고정 소수점 방식에 비하면 덧셈이나 곱셈 계산이 복잡해지고 그만큼 연산시간도 늘어나지만, 대신 표현할 수 있는 범위가 매우 넓다. 대부분의 컴퓨터는 부동 소수점 방식을 사용해 실수를 표현한다.[27]

3.1 IEEE Floating Point Standard (IEEE 754)

IEEE Floating Point Standard (IEEE 754)는 1985년 IEEE에서 공표한 부동 소숫점 방식의 표준안으로, 현재 가장 많은 컴퓨터 시스템에서 실수값을 표현하는데 사용하고 있다. IEEE 754에는 표현하려는 수의 정밀도에 따라 32비트의 단정도(single-precision), 64비트의 배정도(double-precision) 등을 사용할 수 있게 하고 있다. 이 중 32비트 단정도는 실수값을 표현하려는 컴퓨터에서 반드시 구현되어야 하고, 나머지는 선택사항이다. IEEE 754에는 다음과 같은 내용들이 정의되어 있다.

  • 산술 형식(arithmetic formats) : 0을 포함한 일반적인 실수값과 양의 무한대, 음의 무한대, NaN[28] 등의 표현
  • 형식의 교환(interchange formats) : 부동 소수점 데이터를 교환할 때 사용할 수 있는 효율적이고 간편한 인코딩 방식
  • 반올림 규칙(rounding rules) : 산술적인 계산이나 변환 과정에 있어서 반올림 할 때 지켜져야 할 성질
  • 연산(operations) : 산술 형식으로 나타낸 데이터에서의 산술적, 기타 연산
  • 예외 처리(exception handling) : 예외적인 조건의 표기(0으로의 나누는 작업, 오버플로우 등)

이외에도 IEEE 754에는 더 복잡한 예외 처리, 추가적인 작업(삼각함수 등), 수식의 계산 등에 대한 정의가 포함되어 있다.

3.2 실수의 표현

IEEE 754는 실수를 다음 식의 형태로 표현한다.

(-1)s × M × 2E
  • s는 이 수의 부호(sign)를 나타낸다 : 양수일 때 s = 0, 음수일 때 s = 1
  • M은 유효숫자(significant)를 나타낸 값이다. significand라 부른다.
  • E는 지수(exponent)를 뜻한다.

이를 다음과 같이 Encoding한다.

000000000
sexpfrac
  • MSB s는 부호 비트이다.
  • exp는 E를 나타낸다.
  • frac은 M을 나타낸다.

단정도(single-precision)의 경우 8비트로 exp를, 23비트로 frac을 나타낸다.[29] 배정도(double-precision)의 경우 11비트로 exp를, 52비트로 frac을 나타낸다.[30]

IEEE 754에서는 Normalized value, Denormalized value, Special value 세 가지 값들[31]에 대해 다른 인코딩[32] 방법을 적용한다.

3.2.1 Normalized value의 표현

만약 exp의 값이 000...0이나 111...1이 아니라면 이는 Normalized value 형식으로 인코딩된 실수이다.
인코딩 방법은 다음과 같다.

  • exp = E + bias (단, bias = 2k - 1 - 1. k는 exp의 비트 수이다.)[33]
  • frac = (2진법으로 나타낸) M의 소숫점 아래 (유효)숫자들 (이때 M = 1.xxx... 형태를 하고 있다.)[34]

디코딩[35] 방법은 다음과 같다.

  • E = exp - bias
  • M = 1.(frac)

예로서 float f = 2003.0;에서 f에 어떤 2진수 값들이 들어가는지(인코딩 되는지) 알아보자.[36]

우선 10진수 2003.0을 2진수로 바꿔보자.
2003.0 = 11111010011(2) = 1.1111010011(2) × 210
이므로, E = 10, M = 1.1111010011(2)이다.

이 때, exp = E + bias이고, bias = 2k - 1 - 1 = 28 - 1 - 1 = 127이므로 exp = 10 + 127 = 137 = 1000 1001(2)이다.
또한 M = 1.1111010011(2)에서 frac = 111 1010 0110 0000 0000 0000(2)이다.

따라서 2003.0 = 0100 0100 1111 1010 0110 0000 0000 0000(2) = 0x44FA 6000으로 인코딩되어 f에 저장된다.

디코딩은 이를 역순으로 하면 된다.예제쓰기 귀찮다.

3.2.2 Denormalized value의 표현

만약 exp = 000...0이라면 이는 Denormalized value 형식으로 인코딩된 실수이다.
인코딩 방법은 다음과 같다.

  • exp = 000...0 (고정)
  • frac = (2진법으로 나타낸) M의 소숫점 아래 (유효)숫자들 (이때 M = 0.xxx... 형태를 하고 있다.)[37]

디코딩 방법은 다음과 같다.

  • E = 1 - bias
  • M = 0.(frac)

Denormalized value가 표현하는 범위는 0과 그 주위의 (절대값이) 매우 작은 수들이다.
즉, float로 0은 0000 0000 0000 0000 0000 0000 0000 0000(2) = 0x0000 0000 또는 1000 0000 0000 0000 0000 0000 0000 0000(2) = 0x8000 0000으로 표현할 수 있다.[38]

Denormalized value가 표현할 수 있는 float형의 0이 아닌 가장 작은 양수는 0.000 0000 0000 0000 0000 0001(2) × 2-126이다.[39] 이보다 작은 양수는 IEEE 754에서는 표현할 수 없어서 모두 0000 0000 0000 0000 0000 0000 0000 0000(2) = 0x0000 0000으로 인코딩해 버린다.[40] 물론 더 많은 비트를 사용하여 실수를 표현하면 더 작은 수까지도 표현이 가능하다. 이는 IEEE 754의 실수 표현에는 '하한'이 존재한다는 것을 보여준다.

3.2.3 Special value의 표현

만약 exp = 111...1이라면 이는 특수한 숫자들을 표현하기 위해 예약되어 있는 수들이다.

  • exp = 111...1이고 frac = 000...0인 수는 무한(±∞)을 표현하기 위한 수이다.
    • float +∞ = 0111 1111 1000 0000 0000 0000 0000 0000(2) = 0x7F80 0000
    • float -∞ = 1111 1111 1000 0000 0000 0000 0000 0000(2) = 0xFF80 0000
  • exp = 111...1이고 frac ≠ 000...0인 수는 NaN(Not-A-Number)을 표현하기 위한 수이다. [math]\sqrt{-1}[/math](허수), ∞ - ∞, ∞ × 0, 0 ÷ 0 등등의 수를 나타내는데 사용된다.


각 표현 체계들이 나타내는 수의 범위는 다음과 같다. (양수일 때만 서술하겠다. 음수일 때는 반대로 하면 된다.쓰기귀찮아...)

←작다크다→기타
+00 근처의 매우 작은 수들일반적인 실수들+∞NaN
Denormalized valueNormalized valueSpecial value

4 컴퓨터에서 허수 표현하기

허수를 표현하기 위한 특별한 방법은 없다. 허수의 정의상 음수에 제곱근을 씌워야 하는데, 계산 방식상 제곱해서 음수가 되는 수를 계산할 수 없기 때문이다.
울프램알파매스매티카와 같은 프로그램에서는 복소수를 두 실수의 결합으로 취급하는 꼼수를 쓴다.

5 더 보기

  1. 2010년대부터는 AMD x86-64가 궤도에 본격적으로 오르기 시작하면서 64비트 수 표현을 기본으로 지원하게 되었으나, 아직까지는 멀었다. 다만 대부분의 64bit 시스템은 호환성을 위해 32bit 시스템을 지원한다.
  2. 예를 들면 같은 윈도 시스템에서도 .NET Framework의 long형은 64 bit 이다.
  3. 바이트는 컴퓨터가 정보를 저장하는 가장 작은 단위이자 메모리 상에서 주소가 배정될 수 있는(addressable) 가장 작은 단위이다. 메모리는 바이트 단위로 주소가 배정되어 있고(주소가 배정되어 있어야 접근이 가능하기에 중요하다.), 정수는 4바이트이므로 4칸이 필요하다.
  4. 당연하지만 64비트 수 체계는 이의 2배인 8칸을 차지한다.
  5. 표에서 한 칸은 1바이트를 의미한다.
  6. http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/Network_Programing/Documents/endian#AEN57
  7. 000011(2)과 100011(2)을 이진수 계산으로 더하면 000011(2) + 100011(2) = 100110(2) = -6이 되는데, 이는 결과값으로 나와야 할 (+3) + (-3) = 0과는 다른 값이다.
  8. 1의 보수 방법의 정확한 정의는 다음과 같다 : 총 n개의 비트로 정수를 표현할 때, 모든 n비트가 1로 이루어진 수(2n - 1 = 111...11(2))에서 나타내고 싶은 음수의 절댓값을 뺀 수. 1의 보수 방법이라 부르는 이유가 여기에 있다.
  9. 000011(2) + 111100(2) = 111111(2) = 0
  10. 정의대로라면 +0을 의미한다.
  11. 정의대로라면 -0을 의미한다.
  12. 2의 보수 방법의 정확한 정의는 다음과 같다 : 총 n개의 비트로 정스를 표현할 때, 2n = 1000...0(2)에서 나타내고 싶은 음수의 절댓값을 뺀 수. 2의 보수 방법이라 부르는 이유가 여기에 있다.
  13. 2의 보수 방법으로 이진수를 구하는 다른 방법도 있다. -3을 구하는 과정으로 이 방법을 설명하겠다. 우선 이 수의 절대값인 +3을 2진수로 표현한다. +3 = 000011 그리고 왼쪽에서부터 1이 나올 때까지 모든 0을 1로 반전시킨다. 왼쪽에서부터 가장 처음 나오는 1은 0으로 반전시키고 그 이후의 수들은 모두 반전시키지 않는다. 그렇게 나온 수가 2의 보수 방법으로 표현한 음수가 된다. 즉 000011(2)에서 밑줄친 1 이전의 0은 모두 1로, 밑줄친 1은 0으로, 그 이후는 그대로 둬서 111101(2)을 얻어 -3을 표현한다. 다른 예) -5 = -(000101(2)) = 111001(2), -10 = -(001010(2)) = 110010(2)
  14. c언어의 경우 limits.h 헤더파일에 INT_MIN으로 정의되어 있다.
  15. c언어의 경우 limits.h 헤더파일에 INT_MAX로 정의되어 있다.
  16. c언어의 경우 limits.h 헤더파일에 UINT_MAX로 정의되어 있다.
  17. 조금 다르게 표현하면, 값 올림(carry)이 발생하여 w비트로는 표현할 수 없게 된다.
  18. 이런 식으로 계산 결과가 값 올림(carry)으로 표현의 범위를 초과해 잘못된 계산 결과를 출력하는 현상을 오버플로우(overflow)라 한다. 이 오류는 구조적인 문제이므로 근본적인 디버그가 불가능하다.
  19. 이 과정에서 원래의 부호 비트는 버려지면서 계산값의 부호가 바뀐다! 음수를 더했더니 양수가 나오는 기적일이 일어날 수도 있다는 것이다. (이것 역시 오버플로우이다.)
  20. 이렇게 음의 정수 두 개를 더해서 양수가 나오면 이를 음의 오버플로우(Negative overflow)라 부른다.
  21. 이렇게 양의 정수 두 개를 더해서 음수가 나오면 이를 양의 오버플로우(Positive overflow)라 부른다.
  22. 이를 지원하지 않는 컴파일러도 있고, 최적화 옵션에따라 최적화 하지 않을수도 있다.
  23. x << 3 = x * 8, x << 2 = x * 4, x << 1 = x * 2이고, 14 = 8 + 4 + 2이므로
  24. x << 4 = x * 16, x << 1 = x * 2이고, 14 = 16 - 2이므로
  25. 한자로 부동(不動)이 아니라 부동(浮動)이다. 영어 단어 floating 을 직역한 표현인데, 소수점 부호의 위치가 고정되어 있지 않고 떠서 움직인다는 뜻이다.
  26. 물론 위에서 말했듯이 완벽하게 정밀할 수는 없다.
  27. 물론 확실한 정밀도가 필요하다면 프로그래머재량에 따라 소프트웨어적으로 고정 정밀도 소수를 이용할 수도 있다. 물론 이런 경우에는 하드웨어 지원 그런거 없다.
  28. Not a Number의 약자로, 0 ÷ 0 같은 연산의 결과값처럼 수학적으로 계산 불가능한 값을 나타낼 때 쓰인다.
  29. 총 1 + 8 + 23 = 32비트(=4바이트)
  30. 총 1 + 11 + 53 = 64비트(=8바이트)
  31. 표현하는 범위가 다르다
  32. 10진수를 2진수로 바꾸는 과정을 의미한다.
  33. 이렇게 함으로써 exp는 언제나 양수가 된다.
  34. 이렇게 첫 자리 1을 생략함으로써 우리는 유효숫자를 한 자리 더 표현할 수 있게 되었다!
  35. 2진수를 10진수로 바꾸는 과정을 의미한다.
  36. float형은 8비트로 exp를, 23비트로 frac을 나타낸다.
  37. Normalized value를 표현할 때와 다르게 정수부가 1이 아닌 0임에 주의하라!
  38. 엄밀히 표현하면 앞의 수 0x0000 0000은 +0, 뒤의 수 0x8000 0000는 -0을 의미한다.
  39. 0000 0000 0000 0000 0000 0000 0000 0001(2) = 0x0000 0001으로 인코딩된다.
  40. 이를 gradual overflow(점진적 오버플로우)라 한다.