본문 바로가기

진법과 고정 길이 정수

2진수·16진수의 자리값을 계산하고 고정 폭 덧셈·곱셈·나눗셈에서 결과 폭을 정합니다.

8비트 센서가 11001010을 보냈습니다. 이 패턴이 십진수 몇인지 알려면 자리마다 가중치를 붙이면 됩니다. 이 절에서는 먼저 부호 없는 정수로 읽습니다. 같은 패턴의 음수 해석은 다음 절에서 다룹니다.

자리값을 더하면 값이 됩니다

십진수 203은 2 × 10² + 0 × 10¹ + 3 × 10⁰입니다. 이진수도 밑이 2로 바뀔 뿐입니다. 오른쪽부터 1·2·4·8·16·32·64·128의 자리를 읽습니다. 11001010₂ = 128 + 64 + 8 + 2 = 202입니다. 숫자 아래의 ₂는 진법 표시이며 저장 데이터의 일부가 아닙니다.

십진수 13을 이진수로 바꿀 때 2로 나누어 나머지를 적으면 13→6 나머지1, 6→3 나머지0, 3→1 나머지1, 1→0 나머지1입니다. 마지막 나머지부터 읽어 1101을 얻습니다. 작은 값은 자리값 8+4+1로도 확인할 수 있습니다.

16진수는 0부터 9까지의 숫자와 A부터 F까지의 문자로 0부터 15까지의 값을 표현합니다. 이진수 4비트가 16진수 한 자리에 대응합니다. 1100 1010 = CA₁₆입니다. 0xCA는 코드에서 흔히 쓰는 16진수 표기입니다. 8진수 한 자리는 3비트이므로 110 010 10처럼 왼쪽부터 제멋대로 나누지 말고 오른쪽에서 3비트씩 나누고 왼쪽을 0으로 채웁니다.

고정 폭에서는 넘친 자리가 사라집니다

4비트 부호 없는 값의 범위는 0~15입니다. 1011 + 0110 = 10001은 수학적으로 11+6=17이지만, 결과를 4비트 자리에 저장하면 0001과 자리올림 1을 얻습니다. 저장한 값 1만 보고 덧셈이 정확했다고 판단하면 안 됩니다.

시프트(shift)는 비트를 옮기는 연산입니다. 부호 없는 값의 왼쪽 시프트는 범위를 넘지 않는 동안 2배, 오른쪽 논리 시프트는 정수 몫으로 2로 나누는 효과가 있습니다. 4비트 1100을 왼쪽으로 한 칸 옮겨 폭을 제한하면 1000이므로 12×2가 그대로 남지는 않습니다.

곱셈·나눗셈도 폭과 나머지가 필요합니다

부호 없는 4비트 두 값의 곱은 최대 15×15=225이므로 8비트가 있으면 모두 표현됩니다. 101₂ × 11₂은 5×3입니다. 3의 각 비트에 따라 5와 왼쪽으로 한 칸 옮긴 10을 더해 1111₂ = 15를 얻습니다.

나눗셈 13 ÷ 3은 몫 Q=4, 나머지 R=1입니다. 관계식은 13 = 3 × 4 + 1이며 0 ≤ R < 3입니다. 몫과 나머지를 더해 13/3이라고 쓰면 단위와 값이 맞지 않습니다. 이 식은 캐시 인덱스 계산에도 다시 등장합니다.

width = 4
mask = (1 << width) - 1
raw_sum = 11 + 6
print(raw_sum & mask, raw_sum >> width)
assert (raw_sum & mask, raw_sum >> width) == (1, 1)
assert divmod(13, 3) == (4, 1)
assert 13 == 3 * 4 + 1
assert (12 << 1) & mask == 8

<<와 >>는 시프트, & mask는 선택한 하위 비트만 남기는 연산입니다. 출력은 1 1입니다. 코드가 이해되지 않아도 먼저 자리값과 폭을 손으로 계산할 수 있습니다.

독립 문제: 저장 결과와 수학 결과

6비트 부호 없는 레지스터에서 45+27을 계산합니다. 수학 결과, 저장 패턴, 저장 값, 자리올림을 구하세요. 이어 29를 8로 나눈 몫·나머지와 관계식을 쓰세요.

힌트

6비트로 표현할 수 있는 패턴은 64개입니다. 72에서 64가 한 번 빠지는지 보세요.

풀이

45+27=72이고 7비트로 1001000입니다. 하위 6비트는 001000이므로 저장 값은 8, 자리올림은 1입니다. 29=8×3+5이며 몫 3·나머지 5입니다. “정수라서 오차가 없다”는 말은 저장 폭이 충분할 때에만 맞습니다.

다음 절의 음수·보수·넘침에서는 같은 비트를 부호 있는 값으로 읽고, 자리올림과 부호 있는 넘침을 구분합니다.