컴퓨터구조 4편

나눗셈 하드웨어와 부동소수점 — 복원 나눗셈, IEEE 754, 반올림

suhyun·2026년 9월 22일·읽는 데 약 19분
한 줄로: 나눗셈은 빼기와 복원, 반대 방향 이동이고, 부동소수점은 부호와 크기 정보를 나눠 담는다. 둘 다 같은 회로 위에 저장 규칙과 제어가 쌓인 것이다.

1. 손으로 하는 나눗셈

앞 글 끝에 나눗셈과 부동소수점을 남겼습니다. 곱셈에서는 더하기와 자리 이동을 반복했고, 곱 레지스터도 절반씩 나눠 썼습니다. 나눗셈에도 이 구조를 쓸 수 있을까요. 7 ÷ 2로 확인합니다. 나눠지는 수 7을 피제수, 나누는 수 2를 제수, 2가 들어가는 횟수 3을 , 다 나누고 남은 1을 나머지라고 합니다.

7은 0111이고, 2는 0010입니다. 피제수의 왼쪽 자리부터 하나씩 내립니다. 현재까지 내려온 값이 중간 나머지입니다. 첫 자리 0은 2보다 작으니 몫에 0을 적고, 다음 1을 내린 중간값 1도 2보다 작아 몫은 또 0입니다. 다음 1까지 내리면 11, 즉 3이니 2를 빼고 몫에 1을 적습니다. 남은 1에 마지막 1을 내리면 다시 3이라 또 2를 빼고 몫에 1을 적습니다. 몫은 0011, 남은 값은 1입니다. 매번 할 일은 비교하고 빼는 것입니다.

비교 대신 먼저 빼기

회로는 크기를 비교한 뒤 빼야 할까요. 먼저 빼 보고 결과로 판단해도 됩니다. 1에서 2를 빼면 −1입니다. 뺄 수 없었으니 몫의 해당 자리는 0이고, 2를 다시 더하면 원래 1로 돌아옵니다. 이렇게 뺀 값을 되돌리는 동작이 복원입니다. 반대로 3에서 2를 빼면 1이 남고, 음수가 아니니 그대로 두고 몫에 1을 씁니다. 덧셈과 뺄셈 등을 하는 회로인 ALU 안에서 2를 빼는 일도 2의 보수 −2를 더해 처리합니다. 이 나눗셈은 빼기와 복원용 더하기를 반복합니다.

2. 나눗셈 회로 — 버전 1에서 버전 2로

버전 1

버전 1은 레지스터를 제수용, 나머지용, 몫용 3개 둡니다. 입력이 32비트면 앞의 2개는 64비트이고, 몫은 32비트로 두고 0으로 시작합니다. 나머지의 위쪽 절반은 0, 아래쪽 절반에는 피제수를 넣습니다. 제수는 반대로 위쪽 절반에 넣고 아래쪽은 0으로 채웁니다. 4비트 예제에서는 나머지가 위쪽 0000, 아래쪽 0111, 제수가 위쪽 0010, 아래쪽 0000입니다. 제수를 높이 올려 두고 한 자리씩 내려옵니다.

매 회 몫을 왼쪽으로 1칸, 제수를 오른쪽으로 1칸 시프트합니다. 그다음 나머지에서 이동한 제수를 뺍니다. 결과가 음수면 복원하고 몫 끝에 0을, 음수가 아니면 1을 넣습니다. 음수 여부는 맨 왼쪽 비트인 MSB로 판단합니다. 맨 오른쪽 비트는 LSB라고 합니다. 예제에서는 16과 8을 차례로 빼 보는데 둘 다 7보다 커서 복원하고, 다음에는 4를 빼 3이 남고, 마지막에 2를 빼 1이 남습니다. 몫은 0011, 입력 4비트에 맞춰 4번 반복한 결과입니다.

움직이는 쪽 바꾸기

필요한 것은 두 값의 상대적인 위치입니다. 제수를 오른쪽으로 밀든 나머지를 왼쪽으로 밀든 같은 자리에서 겹칩니다. 그래서 제수 0010을 위쪽 4자리에 고정하고, 나머지 전체를 왼쪽으로 1칸씩 밉니다. 아래쪽 피제수의 비트가 위쪽으로 들어오고, 위쪽에 모인 값에서 제수를 빼면 됩니다. 제수를 이동하던 회로 대신 나머지를 이동하는 회로를 둡니다.

이제 제수는 나머지의 위쪽 절반과만 계산합니다. 아래쪽에 대응하는 제수 비트는 0이니 들고 있을 필요가 없습니다. 나머지는 여전히 8비트이고 시프트할 때는 8비트 전체가 움직이지만, 빼거나 복원할 때는 위쪽 4비트만 바뀝니다. 32비트 입력이면 제수는 32비트, 나머지는 64비트이고 ALU는 위쪽 32비트와 제수를 계산합니다. 비트의 이동 범위와 계산 범위를 구별하면 둘을 모두 64비트로 만들 이유가 없습니다.

몫 레지스터까지 합치기

나머지 레지스터를 1번 밀 때마다 피제수 1비트를 소비하고, 오른쪽 끝에 빈자리가 1개 생깁니다. 그 자리에 이번에 정한 몫 비트를 넣습니다. 다음 시프트에서는 그것도 함께 왼쪽으로 가고, 새 빈자리는 다음 차례가 채웁니다. 4번 반복하면 원래 피제수는 모두 위로 이동하고 아래쪽 4자리는 몫으로 채워집니다. 이것이 버전 2입니다. 별도 몫 레지스터와 그 시프트 회로가 사라지고, 32비트 제수와 64비트 합친 레지스터가 남습니다.

3. 문제 — 버전 2로 7 ÷ 2

나머지 레지스터는 위쪽 0000, 아래쪽 피제수 0111로 시작하고, 제수는 0010으로 고정합니다. 매 회 전체를 왼쪽으로 1칸 밀고, 위쪽에서 제수를 빼 봅니다.

회차시프트 후위쪽 − 2몫 비트회차 끝 상태
시작0000 | 0111
10000 | 1110음수 → 복원00000 | 1110
20001 | 1100−1 → 복원00001 | 1100
30011 | 10001, 유지10001 | 1001
40011 | 00101, 유지10001 | 0011

중간의 아래쪽 4자리를 통째로 몫으로 읽으면 안 됩니다. 2회차 끝의 1100에서 앞의 11은 남아 있는 피제수이고, 뒤의 00은 이미 결정한 몫입니다. 시프트가 진행될수록 이 경계가 왼쪽으로 갑니다. 저장 공간을 합쳐도 두 정보가 섞이지 않는 이유입니다. 몫을 따로 기록하는 표라면 3회차에 0001로 보이지만, 합친 회로의 아래쪽은 1001입니다. 합치기 전에는 빈자리를 0으로 뒀고 합친 뒤에는 그 빈자리에 몫이 들어가니, 중간 아래쪽 값이 달라도 위쪽의 빼기 판단과 마지막 답은 같습니다.

끝나면 위쪽 0001이 나머지, 아래쪽 0011이 몫입니다. 피제수 = 몫 × 제수 + 나머지이니 3 × 2 + 1 = 7로 맞고, 나머지 1은 제수 2보다 작아 더 뺄 수 없습니다. 이 회로는 몫이 3이라고 3번 돌지 않습니다. 4비트 입력이므로 4번, 32비트라면 32번 판단합니다. 음수가 나온 횟수는 복원 횟수를 바꿀 뿐, 처리할 비트 수는 달라지지 않습니다.

4. 곱셈과 한 회로로, 그리고 음수

나눗셈 회로를 곱셈 회로와 나란히 봅니다. 곱셈은 64비트 곱 레지스터를 오른쪽으로 밀고, 끝의 LSB를 보고 더할지 정합니다. 나눗셈은 합친 레지스터를 왼쪽으로 밀고, 빼기의 MSB를 보고 복원할지 정합니다. 두 회로 모두 위쪽 32비트에서 계산하고, 고정된 32비트 입력도 각각 1개입니다. ALU가 더하기와 빼기를 선택하게 하고, 시프트 회로가 양쪽 방향을 지원하게 만듭니다. 동작 순서를 정하는 제어 회로가 볼 비트와 이동 방향을 고르면, 같은 저장 공간과 계산기를 공유합니다.

음수가 있는 나눗셈

−7 ÷ 2는 먼저 크기만 써서 7 ÷ 2를 계산합니다. 몫의 크기는 3, 나머지의 크기는 1입니다. 몫은 0을 향해 소수 부분을 버리고, 두 입력의 부호가 다르면 음수입니다. 나머지의 부호는 피제수를 따릅니다.

  • −7 ÷ 2 → 몫 −3, 나머지 −1 (−3 × 2 − 1 = −7)
  • 7 ÷ −2 → 몫 −3, 나머지 1
  • −7 ÷ −2 → 몫 3, 나머지 −1

정수 나눗셈은 이렇게 남은 값을 따로 둡니다. 소수 부분까지 담으려면 수의 저장법을 넓혀야 합니다.

5. 부동소수점과 정규화

7 ÷ 2를 소수로 적으면 3.5입니다. 아주 작은 0.000000001도 필요하고, 3.15576 × 10⁹처럼 큰 수도 있습니다. 소수점 위치를 고정하면 한쪽 공간이 부족합니다. 그래서 숫자의 모양과 크기를 나눠 저장해 소수점의 위치를 따로 조절합니다. 이 방법이 부동소수점 표현이고, 부동은 위치가 고정되지 않았다는 뜻입니다.

3.14는 3.14 × 10⁰입니다. 앞의 3.14가 계수, 10이 밑, 몇 번 곱할지를 나타내는 0이 지수입니다. 음수 지수는 그만큼 나눈다는 뜻이라 10⁻²는 1을 100으로 나눈 값입니다. 같은 수를 0.314 × 10¹로도 쓸 수 있으니 표기를 통일하는 규칙이 필요합니다. 이것이 정규화입니다. 십진수에서는 계수를 1 이상 10 미만, 2진수에서는 1 이상 2 미만으로 둡니다. 그래서 0이 아닌 2진수의 계수는 1로 시작합니다. 0.11₂를 정규화하면 1.1₂ × 2⁻¹이고, 소수점을 옮긴 만큼 지수가 값을 보상합니다.

6. IEEE 754 — 부호, 지수, fraction

부동소수점의 비트 배치를 정한 표준이 IEEE 754입니다. 부호와 지수, 소수 부분을 각각의 필드에 저장합니다.

  • 단정밀도 (32비트) — 부호 1비트, 지수 8비트, fraction 23비트. bias 127
  • 배정밀도 (64비트) — 부호 1비트, 지수 11비트, fraction 52비트. bias 1023

부호는 0이면 양수, 1이면 음수이고, 나머지 비트가 크기를 설명합니다. 정수처럼 전체를 2의 보수로 뒤집지 않습니다.

저장하지 않는 맨 앞의 1

정규화한 계수를 유효숫자부, significand라고 합니다. 1.1₂ × 2⁻¹에서는 1.1₂이고, 소수점 아래만 뗀 0.1₂를 fraction이라고 부릅니다. 정규화한 2진수의 맨 앞은 항상 1이니 매번 저장할 필요가 없습니다. fraction만 저장하고 읽을 때 앞에 1을 붙입니다. 이 생략된 자리가 숨은 1입니다. 단정밀도는 fraction 23비트에 숨은 1까지 유효한 자리가 24개입니다.

지수에 기준값 더하기

지수는 음수도 저장할 수 있어야 합니다. 그래서 지수에 일정한 수, bias를 더해 저장합니다. 단정밀도에서 실제 지수 −1은 126으로, 0은 127로, 1은 128로 저장하고, 읽을 때는 127을 뺍니다. 합치면 값은 이렇게 읽힙니다.

(−1)^부호 × (1 + fraction) × 2^(지수 필드 − bias)

7. 문제 — 32비트에 담고 읽기

2진 소수의 자리값은 1/2, 1/4, 1/8로 오른쪽으로 갈 때마다 절반이 됩니다.

−0.75

0.75는 1/2 + 1/4이므로 0.11₂, 정규화하면 1.1₂ × 2⁻¹입니다. 부호는 1, 지수 필드는 −1 + 127 = 126, fraction은 첫 자리만 1입니다. 다시 읽으면 지수는 126 − 127 = −1, 숨은 1을 붙인 계수 1.1₂는 1.5이고 그 절반이 0.75, 부호를 적용하면 −0.75입니다.

1 | 01111110 | 10000000000000000000000

−3.3125

3 + 0.25 + 0.0625이니 11.0101₂이고, 정규화하면 1.10101₂ × 2¹입니다. 부호 1, 지수 필드 128, fraction은 앞 5자리가 10101입니다.

1 | 10000000 | 10101000000000000000000

비트에서 읽기 — −5.0

부호 1, 지수 필드 129, fraction이 010으로 시작하고 나머지가 0입니다. 실제 지수는 129 − 127 = 2, 숨은 1을 붙인 계수는 1.01₂ = 1.25, 2²을 곱하면 5, 부호를 적용하면 −5.0입니다. fraction을 그대로 계수로 쓰거나 129를 그대로 지수로 쓰면 틀립니다. 생략한 1과 더해 둔 127을 각각 복구해야 합니다.

−12.75

12는 1100₂, 0.75는 0.11₂이니 1100.11₂입니다. 소수점을 왼쪽으로 3칸 옮기면 1.10011₂ × 2³, 지수 필드는 130입니다.

1 | 10000010 | 10011000000000000000000

세 예제 모두 필요한 자리가 23비트 안에 들어서 반올림할 필요가 없습니다.

8. 범위, 정밀도, 특수값

숨은 1을 붙여 읽는 수를 정규수라고 합니다. 단정밀도 정규수의 지수 필드는 1~254이고, 양 끝의 0과 255는 특별한 표현에 씁니다. 지수 필드 1, fraction 0이면 가장 작은 양의 정규수 2⁻¹²⁶, 약 1.2 × 10⁻³⁸입니다. 지수 필드 254, fraction을 모두 1로 채우면 계수가 2에 조금 못 미치고, 최댓값은 약 3.4 × 10³⁸입니다. 배정밀도는 지수 필드 1~2046을 쓰고, 최소 2⁻¹⁰²² 약 2.2 × 10⁻³⁰⁸, 최대 약 1.8 × 10³⁰⁸입니다. 지수 비트가 늘면 소수점 이동 폭이 커집니다.

정밀도는 값 사이의 간격

가까운 두 수를 얼마나 구별하는지가 정밀도입니다. 단정밀도로 저장한 1에서 fraction 마지막 비트만 1로 바꾸면 차이는 2⁻²³, 약 0.000000119입니다. 이 간격 때문에 십진수 약 7자리 정밀도라고 합니다. 7자리를 언제나 정확히 담는다는 말은 아닙니다. 배정밀도는 대략 16자리입니다. 실제 지수가 1 증가하면 간격도 2배가 됩니다. 지수는 표현 범위를, fraction은 같은 크기 주변의 촘촘함을 정합니다.

지수 필드의 양 끝

  • 지수 0 — 비정규수와 0. 숨은 1 대신 0을 붙이고, 실제 지수는 −126으로 고정합니다. 값은 fraction × 2⁻¹²⁶이고, fraction 마지막 비트만 1이면 2⁻¹⁴⁹가 가장 작은 양의 단정밀도 값입니다. 작은 결과를 충분히 담지 못하는 언더플로를 비정규수가 0 쪽 간격을 메워 줄여 줍니다. fraction까지 모두 0이면 정확한 0이고, 부호에 따라 +0과 −0입니다.
  • 지수 255 — 무한대와 NaN. fraction이 모두 0이면 무한대이고, 부호로 양과 음을 나눕니다. fraction이 0이 아니면 NaN, 0 ÷ 0처럼 수로 정할 수 없는 결과입니다. 지수 필드만 보고는 둘을 구별할 수 없고, fraction이 0인지까지 확인해야 합니다.

9. 덧셈과 반올림 — G·R·S

부동소수점 덧셈은 같은 자리끼리 더해야 합니다. 9.999 × 10¹에 1.610 × 10⁻¹을 더할 때 계수를 바로 더하면, 같은 자리가 실제로는 다른 크기입니다. 작은 수의 지수를 1로 맞추고 계수를 오른쪽으로 2칸 밀어 0.01610 × 10¹로 만든 뒤 더합니다. 결과 10.01510 × 10¹은 정규화 범위를 벗어나니 1.001510 × 10²로 옮기고, 유효숫자 4자리만 남기면 뒤가 510이라 1.002 × 10²로 반올림합니다. 반올림으로 계수가 커지면 한 번 더 정규화하고 범위도 확인합니다.

밀려난 자리를 남겨야 하는 이유

2.56 × 10⁰과 2.34 × 10²을 더하고 소수점 아래 2자리까지 남깁니다. 작은 쪽을 0.0256으로 맞춘 뒤 바로 0.02로 잘라 버리면 결과는 2.36 × 10²입니다. 0.0256을 유지하고 더하면 2.3656, 마지막에 반올림하면 2.37 × 10²입니다. 저장할 자릿수 밖의 정보도 판단에 필요합니다. 2진수 계산에서는 여분 비트로 이를 보관합니다.

  • G (guard) — 남길 자리 바로 다음 비트
  • R (round) — 그다음 비트
  • S (sticky) — 그 뒤에 밀려난 비트를 OR로 묶은 것. 1이 하나라도 있으면 1

1.001 뒤에 1010이 이어지고 소수점 아래 3자리까지 남긴다면, G는 1, R은 0, 남은 10에 1이 있으니 S는 1입니다. 뒤쪽 전체를 저장하지 않아도 흔적이 남습니다.

가장 가까운 값 고르기

소수점 아래 3자리만 남긴다면 1.001₂ 다음 값은 1.010₂이고, 정확한 중간은 1.0011₂입니다.

  1. G가 0이면 중간보다 작으니 내립니다. R이나 S가 1이어도 중간을 넘지 않습니다. 1.00101₂는 G 0, R 1, S 0이라 1.001₂로 남습니다.
  2. G가 1이고 R이나 S가 1이면 중간보다 크니 올립니다. 1.001101₂는 G 1, R 0, S 1이라 1.010₂로 올립니다. S가 멀리 떨어진 1의 존재를 알려 줍니다.
  3. G가 1이고 R과 S가 0이면 정확히 중간입니다. 마지막 보존 비트가 0인 쪽, 즉 가까운 짝수를 고릅니다. 1.0011₂는 1.010₂로 올리고, 1.0101₂는 1.010₂로 유지합니다. 반복 계산에서 한쪽으로 치우침을 줄이는 선택입니다.

10. 문제 — 2진 부동소수점 덧셈

부호가 다른 덧셈

1.000₂ × 2⁻¹에 −1.110₂ × 2⁻²를 더합니다. 유효숫자는 4자리입니다. 작은 쪽 지수를 −1로 맞추면 −0.111₂이고, 1.000₂에서 빼면 0.001₂ × 2⁻¹입니다. 소수점을 오른쪽으로 3칸 옮겨 지수를 3 줄이면 1.000₂ × 2⁻⁴, 십진수 0.0625입니다. 가까운 크기를 빼면 앞자리가 사라지고, 그만큼 왼쪽으로 당겨 정규화합니다.

자리올림이 생기는 덧셈

소수점 아래 4비트를 보존하고 1.0110₂ × 2³에 1.1000₂ × 2²을 더합니다. 작은 쪽을 0.1100₂로 맞춰 더하면 10.0010₂, 오른쪽으로 1칸 밀어 1.0001₂ × 2⁴입니다. 밀린 비트가 0이라 반올림은 없습니다. 11 더하기 6, 17입니다.

둘째 수만 1.1001₂ × 2²로 바꾸면 작은 쪽이 0.11001₂ × 2³이 되고, 정확한 합 17.25는 1.000101₂ × 2⁴입니다. 남길 계수는 1.0001₂, 밀려난 01에서 G 0, R 1, S 0이니 올리지 않습니다. 결과는 17로, 입력이 0.25 커졌는데도 보존 자릿수에서 두 답은 같습니다.

3칸 이동

소수점 아래 5비트를 보존하고 1.01010₂ × 2³에 1.00111₂ × 2⁶을 더합니다. 작은 쪽을 3칸 밀면 0.00101010₂, 남기는 부분은 0.00101₂이고 밀려난 010에서 G 0, R 1, S 0입니다. 보존 부분끼리 더하면 1.01100₂, G가 0이라 올리지 않아 1.01100₂ × 2⁶, 즉 88입니다. 정확한 합 78 + 10.5 = 88.5의 0.5는 이 정밀도의 반올림에서 사라집니다. 여분 비트는 올리지 않는 판단에도 필요합니다.

11. 부동소수점 곱셈

같은 밑의 거듭제곱을 곱하면 지수를 더합니다. 2³ × 2²은 2⁵입니다. 그런데 저장된 지수에는 bias가 붙어 있습니다. 실제 지수 3은 130, 2는 129로 저장되니 둘을 더하면 127이 2번 들어갑니다. 그래서 저장 지수의 합에서 127을 한 번 뺍니다. 130 + 129 − 127 = 132이고, 읽으면 실제 지수 5입니다. 계수의 곱은 따로 구하고, 부호는 같으면 양수, 다르면 음수입니다.

1.0110₂ × 2³과 1.1100₂ × 2²을 곱하면 계수는 10.01101000₂로 2 이상이니, 오른쪽으로 1칸 밀어 1.00110100₂로 만들고 실제 지수를 6, 저장 지수를 133으로 올립니다. 두 입력이 양수이니 부호는 0, 답은 1.001101₂ × 2⁶입니다. 단정밀도에 소수 부분이 모두 들어가 반올림이 필요 없습니다. 긴 곱이라면 G·R·S를 보고, 반올림으로 계수가 2가 되면 다시 조정하고, 지수 범위도 최종 값에서 확인합니다.

문제 — 1.010₂ × 2⁴ 곱하기 1.001₂ × 2⁶

실제 지수는 4 + 6 = 10입니다. 계수 1.25와 1.125의 곱은 1.40625, 2진수로 1.01101₂이고 이미 1 이상 2 미만입니다. 답은 1.01101₂ × 2¹⁰, 십진수 1440입니다. 부호 0, 지수 필드는 10 + 127 = 137입니다.

0 | 10001001 | 01101000000000000000000

정리

네 편에 걸쳐 명령어와 회로를 이어 봤습니다. 명령어의 비트는 해야 할 동작을 지정하고, 제어 회로는 데이터를 보낼 곳을 고릅니다. 그 안에서 정수 덧셈은 자리올림을 처리하고, 곱셈은 더하기와 시프트를 반복하고, 나눗셈은 빼기와 복원, 반대 방향 이동입니다. 부동소수점은 부호와 크기 정보를 나눠 담습니다. 덧셈에는 지수 맞추기가, 곱셈에는 지수의 덧셈이 붙고, 정규화와 반올림도 비트 이동과 덧셈을 씁니다. −12.75를 담는 형식은 정수와 다르지만, 그 값을 다루는 기본 부품은 이어집니다. 같은 회로 위에 저장 규칙과 제어가 쌓입니다.

dev-news학습 노트소개개인정보 처리