컴퓨터는 소수를 어떻게 표현할까? 컴퓨터는 모든 수를 이진수로 표현한다. 그렇다면 우리가 흔히 쓰는 소수들은 어떻게 표현할까?
📌 고정 소수점 (Fixed-Point)
그냥 이진수 그대로 사용하면 소수를 표현할 수는 있다. 예를 들면 8비트의 숫자가 있다면 4비트는 정수 부분 4비트는 소수 부분으로 나눌 수 있겠다.
0 0 0 0 (정수) 0 0 0 0 (소수)
이렇게 되면 3.75 같은 수는 아래와 같이 나타낼 수 있다. (소수를 표현하는 데에 한계는 아랫부분에서 다룰 예정이다)
정수 부분: 0011 (1 + 2 = 3)
소수 부분: 1100 (0.5 + 0.25 = 0.75)
3.75 = 0011 1100
하지만 이렇게 소수점 부분을 고정하게 된다면 8비트의 경우 -128 ~ 127 까지 표현할 수 있었지만 4비트 4비트로 나누면 -32 ~ 31 까지밖에 표현하지 못하게 된다. 결국 소수점을 고정해 버린다면 한정된 비트로 나타낼 수 있는 숫자가 너무나도 줄어들게 된다.
심지어 소수점에 많은 비트를 할당하지 못한다면 정말 적은 숫자로 소수점을 표현해야 하는데 한정된 숫자로 소수를 절-대 표현할 수 없을 것이다. 결국 표현할 수 있는 수의 범위는 너무나도 줄어들게 된다.
🛳️ 부동 소수점 (Floating-Point)
이러한 문제를 보완하고자 부동 소수점이 등장하게 되었다.
🛥️ 부동 소수점의 부는 "뜰 부"
부동 소수점에서 뭔가 부동이라는 단어가 아닐 부가 먼저 생각나서 움직이질 않는데 왜 Floating?이라고 생각할 수도 있지만 (나는 그랬다)
부동 소수점에 부는 浮(뜰 부) 를 사용한다. 개인적으로 차라리 유동 소수점...이라는 이름이 더 와닿는 느낌이 든다. 아무튼 소수점이 숫자 사이를 이동한다고 해서 Floating-Point가 등장하게 되었다!
🧐 어떻게 떠다니는데? (IEEE 754)
전기전자공학자협회(Institute of Electrical and Electronics Engineers, IEEE)에서 나온 IEEE 754 표준에서 알 수 있다. 가장 널리 쓰이는 표준이라고 한다. 가장 최신버전은 2019년 버전인 듯하다. 뭔가 원본 문서를 살펴보고 싶었는데 구입을...(?) 해야 하는 것 같아서 위키피디아를 참고해서 정리하고자 한다.
IEEE Standards Association
These standards have been replaced with a revised version of the standard, or by a compilation of the original active standard and all its existing amendments, corrigenda, and errata. 754-1985 IEEE Standard for Binary Floating-Point Arithmetic A family of
standards.ieee.org
우선 32비트가 기준이 된다. 여기서 최상위 비트는 부호를 표시한다. 8비트는 지수(exponent) 부분이고, 나머지 23비트는 가수(fraction/mantissa)로 구성되어 있다.
0 00000000 00000000000000000000000
│ │ │
S Exponent Fraction/Mantissa (23비트)
(1비트) (8비트)
만약에 우리는 9.625라는 숫자가 있다고 해보자. 이것을 2진수로 바꾸게 된다면 0은 생략한다고 생각하면 정수부분은 1001 소수 부분은 101이 될 것이다.
9.625 = 1001 101
정수 부분: 1001 (8 + 1 = 9)
소수 부분: 101 (0.5 + 0.125 = 0.625)
여기서 정수부분 한자리만 빼고 모두 소수 부분으로 보내버린다면 3자리를 보내버렸으니 아래와 같이 표현할 수 있겠다. 이것을 정규화된 부동 소수점이라고 한다.
1 001101 * 2^3
여기서 3에 32비트의 Bias 127을 더해 130을 만들어 이 값을 지수 부분에 저장한다. 실제 사용가능한 범위는 -126 ~ 127이기 때문에 127을 더해줌으로써 모두 양수로 표현하기 위함이다. (64비트의 경우 Bias는 1023)
10000010 (3 + 127 = 130)
왜 127을 더하는지 궁금해 할 수 있는데, 결국 -128 ~ 127 의 범위는 0 ~ 255 범위와 같고, 0인 경우 숫자 0이나 극소수, 지수 부분이 255인 경우 무한대 및 NaN으로 사용된다고 한다. 따라서 사용할 수 있는 범위는 1 ~254 로 제한된다. 결국 -127 ~ 126 범위를 지원해주느냐 -126 ~ 127 범위를 지원해주느냐 인데 숫자를 더 크게 하는것이 중요하다고 생각한 듯 하다.
안 쓰는 부분은 0으로 채워주니까 결국 아래와 같이 9.625를 나타낼 수 있게 되었다!
0 | 10000010 | 00110100000000000000000
이것을 Swift에서 Float이라는 값 타입으로 여러가지를 확인 해 볼 수 있었다. 이제야 각 단어들이 이해가 가기 시작한다.
print(Float.greatestFiniteMagnitude) // 3.4028235e+38
print(Float.leastNormalMagnitude) // 1.1754944e-38
print(Float.leastNonzeroMagnitude) // 1e-45
print(Float.zero) // 0.0
🤔 Int32와 Float의 차이?
둘 다 32비트인데 Float이 범위가 훨씬 넓으니까 Float으로 대통합! 하면 되는 거 아닌가?라는 생각을 잠깐 해봤지만 오산이다. Float은 모든 정수를 정확히 표현할 수 없다.
아래로 실제로 Swift를 통해 실험을 해볼 수 있었다. 16777217이라는 값을 Float으로 바꾸어 보니 16777216이 되었다...?
let intValue: Int32 = 16777217
let floatValue: Float = Float(intValue)
print(floatValue) // 16777216.0
그 이유는 Float의 구조를 살펴보면 알 수 있다. 23비트의 Fraction 부분이 있기 때문에 아까 정규화했을 때 봤던 1도 포함하게 된다면(암시적 1 포함) 즉 2의 24 제곱인 16777216이 사실 표현 가능한 최대 정수인 것이다.
0 00000000 00000000000000000000000
│ │ │
S Exponent Fraction/Mantissa (23비트)
(1비트) (8비트)
좀 더 프린트해본다면 2의 배수만 표현 가능하구나를 알 수 있다. 숫자가 올라갈수록 표현 가능한 정수의 범위가 늘어나게 된다. 예를 들면 2의 25 제곱부터는 4의 배수만 표현이 가능하다.
print(Float(16777216)) // 16777216.0
print(Float(16777217)) // 16777216.0
print(Float(16777218)) // 1.6777218e+07
print(Float(16777219)) // 1.677722e+07
print(Float(16777220)) // 1.677722e+07
print(Float(16777221)) // 1.677722e+07
print(Float(16777222)) // 1.6777222e+07
😱 소수 0.1을 표현할 수 없다...
0.1 은 어떻게 나타낼 수 있을까? 여기서 이진법이 한계가 나타난다. 소수 부분으로만 표현한다면 0.1을 정확하게 표현할 수 없다.
0.1 = 00011001100110011001100110011...(소수 부분)
8비트: 00011001 = 0.09765625
16비트: 0001100110011001 = 0.09999847412
24비트: 000110011001100110011001 = 0.09999999985
이것과 관련된 유명한 사건으로 1991년 패트리어트 미사일 요격 실패 사고가 있다. 패트리어트 미사일은 0.1초 주기로 시간을 측정하고 있었는데, 장비를 100시간 연속으로 가동하다보니 0.3433초의 오차가 발생했다.(초당 0.000000095초의 오차) 결국 687미터의 거리측정이 잘못되어 요격을 못해 사고가 발생했다.
패트리어트 미사일/실전
패트리어트 미사일 의 실전 기록을 정리한 문서. 걸프 전쟁 1991년 요격 실패 사고 패트리어트 미사일은 1991년
namu.wiki
🔢 Decimal
정확한 10진 연산을 위해서 Decimal128 표준을 따르는 형식이 나오게 되었다. 물론 2진법을 사용하지 않기 때문에 성능면에서 느리고 소프트웨어 구현에 가깝기 때문에 정확도가 최우선이 아닌 경우에는 사용하지 않는다고 한다. 보통 금융 시스템, 회계 소프트웨어에서 활용한다고 한다.
decimal128 floating-point format - Wikipedia
From Wikipedia, the free encyclopedia 128-bit computer number format In computing, decimal128 is a decimal floating-point number format that occupies 128 bits in memory. Formally introduced in IEEE 754-2008,[1] it is intended for applications where it is
en.wikipedia.org
'→ Computer Science' 카테고리의 다른 글
[CS] SSD의 구조 (0) | 2025.04.01 |
---|---|
[CS] HDD의 구조 및 스케줄링 (0) | 2025.03.26 |
[CS] 음수의 표현 (2의 보수) (0) | 2025.03.12 |
[CS] 데이터의 전송 (0) | 2025.02.10 |
[CS] 정보단위 (0) | 2025.01.31 |
컴퓨터는 소수를 어떻게 표현할까? 컴퓨터는 모든 수를 이진수로 표현한다. 그렇다면 우리가 흔히 쓰는 소수들은 어떻게 표현할까?
📌 고정 소수점 (Fixed-Point)
그냥 이진수 그대로 사용하면 소수를 표현할 수는 있다. 예를 들면 8비트의 숫자가 있다면 4비트는 정수 부분 4비트는 소수 부분으로 나눌 수 있겠다.
0 0 0 0 (정수) 0 0 0 0 (소수)
이렇게 되면 3.75 같은 수는 아래와 같이 나타낼 수 있다. (소수를 표현하는 데에 한계는 아랫부분에서 다룰 예정이다)
정수 부분: 0011 (1 + 2 = 3)
소수 부분: 1100 (0.5 + 0.25 = 0.75)
3.75 = 0011 1100
하지만 이렇게 소수점 부분을 고정하게 된다면 8비트의 경우 -128 ~ 127 까지 표현할 수 있었지만 4비트 4비트로 나누면 -32 ~ 31 까지밖에 표현하지 못하게 된다. 결국 소수점을 고정해 버린다면 한정된 비트로 나타낼 수 있는 숫자가 너무나도 줄어들게 된다.
심지어 소수점에 많은 비트를 할당하지 못한다면 정말 적은 숫자로 소수점을 표현해야 하는데 한정된 숫자로 소수를 절-대 표현할 수 없을 것이다. 결국 표현할 수 있는 수의 범위는 너무나도 줄어들게 된다.
🛳️ 부동 소수점 (Floating-Point)
이러한 문제를 보완하고자 부동 소수점이 등장하게 되었다.
🛥️ 부동 소수점의 부는 "뜰 부"
부동 소수점에서 뭔가 부동이라는 단어가 아닐 부가 먼저 생각나서 움직이질 않는데 왜 Floating?이라고 생각할 수도 있지만 (나는 그랬다)
부동 소수점에 부는 浮(뜰 부) 를 사용한다. 개인적으로 차라리 유동 소수점...이라는 이름이 더 와닿는 느낌이 든다. 아무튼 소수점이 숫자 사이를 이동한다고 해서 Floating-Point가 등장하게 되었다!
🧐 어떻게 떠다니는데? (IEEE 754)
전기전자공학자협회(Institute of Electrical and Electronics Engineers, IEEE)에서 나온 IEEE 754 표준에서 알 수 있다. 가장 널리 쓰이는 표준이라고 한다. 가장 최신버전은 2019년 버전인 듯하다. 뭔가 원본 문서를 살펴보고 싶었는데 구입을...(?) 해야 하는 것 같아서 위키피디아를 참고해서 정리하고자 한다.
IEEE Standards Association
These standards have been replaced with a revised version of the standard, or by a compilation of the original active standard and all its existing amendments, corrigenda, and errata. 754-1985 IEEE Standard for Binary Floating-Point Arithmetic A family of
standards.ieee.org
우선 32비트가 기준이 된다. 여기서 최상위 비트는 부호를 표시한다. 8비트는 지수(exponent) 부분이고, 나머지 23비트는 가수(fraction/mantissa)로 구성되어 있다.
0 00000000 00000000000000000000000
│ │ │
S Exponent Fraction/Mantissa (23비트)
(1비트) (8비트)
만약에 우리는 9.625라는 숫자가 있다고 해보자. 이것을 2진수로 바꾸게 된다면 0은 생략한다고 생각하면 정수부분은 1001 소수 부분은 101이 될 것이다.
9.625 = 1001 101
정수 부분: 1001 (8 + 1 = 9)
소수 부분: 101 (0.5 + 0.125 = 0.625)
여기서 정수부분 한자리만 빼고 모두 소수 부분으로 보내버린다면 3자리를 보내버렸으니 아래와 같이 표현할 수 있겠다. 이것을 정규화된 부동 소수점이라고 한다.
1 001101 * 2^3
여기서 3에 32비트의 Bias 127을 더해 130을 만들어 이 값을 지수 부분에 저장한다. 실제 사용가능한 범위는 -126 ~ 127이기 때문에 127을 더해줌으로써 모두 양수로 표현하기 위함이다. (64비트의 경우 Bias는 1023)
10000010 (3 + 127 = 130)
왜 127을 더하는지 궁금해 할 수 있는데, 결국 -128 ~ 127 의 범위는 0 ~ 255 범위와 같고, 0인 경우 숫자 0이나 극소수, 지수 부분이 255인 경우 무한대 및 NaN으로 사용된다고 한다. 따라서 사용할 수 있는 범위는 1 ~254 로 제한된다. 결국 -127 ~ 126 범위를 지원해주느냐 -126 ~ 127 범위를 지원해주느냐 인데 숫자를 더 크게 하는것이 중요하다고 생각한 듯 하다.
안 쓰는 부분은 0으로 채워주니까 결국 아래와 같이 9.625를 나타낼 수 있게 되었다!
0 | 10000010 | 00110100000000000000000
이것을 Swift에서 Float이라는 값 타입으로 여러가지를 확인 해 볼 수 있었다. 이제야 각 단어들이 이해가 가기 시작한다.
print(Float.greatestFiniteMagnitude) // 3.4028235e+38
print(Float.leastNormalMagnitude) // 1.1754944e-38
print(Float.leastNonzeroMagnitude) // 1e-45
print(Float.zero) // 0.0
🤔 Int32와 Float의 차이?
둘 다 32비트인데 Float이 범위가 훨씬 넓으니까 Float으로 대통합! 하면 되는 거 아닌가?라는 생각을 잠깐 해봤지만 오산이다. Float은 모든 정수를 정확히 표현할 수 없다.
아래로 실제로 Swift를 통해 실험을 해볼 수 있었다. 16777217이라는 값을 Float으로 바꾸어 보니 16777216이 되었다...?
let intValue: Int32 = 16777217
let floatValue: Float = Float(intValue)
print(floatValue) // 16777216.0
그 이유는 Float의 구조를 살펴보면 알 수 있다. 23비트의 Fraction 부분이 있기 때문에 아까 정규화했을 때 봤던 1도 포함하게 된다면(암시적 1 포함) 즉 2의 24 제곱인 16777216이 사실 표현 가능한 최대 정수인 것이다.
0 00000000 00000000000000000000000
│ │ │
S Exponent Fraction/Mantissa (23비트)
(1비트) (8비트)
좀 더 프린트해본다면 2의 배수만 표현 가능하구나를 알 수 있다. 숫자가 올라갈수록 표현 가능한 정수의 범위가 늘어나게 된다. 예를 들면 2의 25 제곱부터는 4의 배수만 표현이 가능하다.
print(Float(16777216)) // 16777216.0
print(Float(16777217)) // 16777216.0
print(Float(16777218)) // 1.6777218e+07
print(Float(16777219)) // 1.677722e+07
print(Float(16777220)) // 1.677722e+07
print(Float(16777221)) // 1.677722e+07
print(Float(16777222)) // 1.6777222e+07
😱 소수 0.1을 표현할 수 없다...
0.1 은 어떻게 나타낼 수 있을까? 여기서 이진법이 한계가 나타난다. 소수 부분으로만 표현한다면 0.1을 정확하게 표현할 수 없다.
0.1 = 00011001100110011001100110011...(소수 부분)
8비트: 00011001 = 0.09765625
16비트: 0001100110011001 = 0.09999847412
24비트: 000110011001100110011001 = 0.09999999985
이것과 관련된 유명한 사건으로 1991년 패트리어트 미사일 요격 실패 사고가 있다. 패트리어트 미사일은 0.1초 주기로 시간을 측정하고 있었는데, 장비를 100시간 연속으로 가동하다보니 0.3433초의 오차가 발생했다.(초당 0.000000095초의 오차) 결국 687미터의 거리측정이 잘못되어 요격을 못해 사고가 발생했다.
패트리어트 미사일/실전
패트리어트 미사일 의 실전 기록을 정리한 문서. 걸프 전쟁 1991년 요격 실패 사고 패트리어트 미사일은 1991년
namu.wiki
🔢 Decimal
정확한 10진 연산을 위해서 Decimal128 표준을 따르는 형식이 나오게 되었다. 물론 2진법을 사용하지 않기 때문에 성능면에서 느리고 소프트웨어 구현에 가깝기 때문에 정확도가 최우선이 아닌 경우에는 사용하지 않는다고 한다. 보통 금융 시스템, 회계 소프트웨어에서 활용한다고 한다.
decimal128 floating-point format - Wikipedia
From Wikipedia, the free encyclopedia 128-bit computer number format In computing, decimal128 is a decimal floating-point number format that occupies 128 bits in memory. Formally introduced in IEEE 754-2008,[1] it is intended for applications where it is
en.wikipedia.org
'→ Computer Science' 카테고리의 다른 글
[CS] SSD의 구조 (0) | 2025.04.01 |
---|---|
[CS] HDD의 구조 및 스케줄링 (0) | 2025.03.26 |
[CS] 음수의 표현 (2의 보수) (0) | 2025.03.12 |
[CS] 데이터의 전송 (0) | 2025.02.10 |
[CS] 정보단위 (0) | 2025.01.31 |