성운
[Javascript] 컴퓨터에서의 소수와 JS의 Number 자료형 본문
컴퓨터에서의 실수
컴퓨터에서 실수를 표현하는 방식은 고정 소수점, 부동 소수점 두 가지 방식이 존재합니다.
그러나 고정 소수점의 표현 범위가 너무 작아 큰 소수를 표현할 수 없기 때문에 부동 소수점이 보통 더 자주 사용됩니다.
그래서 부동 소수점에 대한 연산, 표현 등에 대해서는 IEEE 754표준이 정의되어 있으며, 대부분의 프로그래밍 언어가 이 표준을 준수하고 있습니다.
그리고 javascript도 이 표준을 준수하고 있고, JS의 자료형 중 Number 는 이 표준을 준수하는 배정밀도 부동 소수점 방식입니다.
고정 소수점
고정 소수점은 앞에서 표현할 수 있는 범위가 넓지 않다고 했습니다. 32 비트 단정밀도 고정 소수점의 경우 1/15/16으로 부호부/정수부/실수부로 나뉩니다. 정수부로 소수점 왼쪽의 값을 표현하고 실수부로 소수점 오른쪽의 값을 표현합니다. 따라서 단정밀도 고정 소수점의 경우 표현할 수 있는 최대 정수가 2^15 - 1 이죠.
부동 소수점
부동 소수점은 영어로 floating point라고 하는데요. 이름 그대로 소수점이 떠다니는, 즉 점이 고정되어있지 않고 유동적으로 움직일 수 있는 방식을 말합니다. 그래서 만약에 더 큰 수를 나타내고 싶다면 소수점을 뒤로 밀어서 더 넓은 값을 표현할 수 있으며, 좀 더 정밀한 소수 부분을 표현하고 싶으면 소수점을 왼쪽으로 밀어서 표현할 수 있죠.
그래서 고정 소수점과는 다르게 더 넓은 범위의 값을 표현할 수 있습니다. 주로 사용되는 부동 소수점은 배정밀도 부동 소수점인데요. 32비트의 배인 64비트로 부동 소수점을 표현합니다. 이때 총 비트를 1/11/52로 나눠 부호부/지수부/가수부로 사용합니다.
부호는 +/- 구분을 나타냅니다. 음수일 경우 1입니다.
지수부는 부동 소수점도 어쨌든 2진수 이기 때문에 숫자를 10101010.1011001 식으로 변환하는데요. 이때 중간에 있는 소수점을 왼쪽으로 최대한 당겨서 남은 숫자가 1.xx 로 만든다고 할 때, 몇 칸을 어느 방향으로 옮기는지를 말합니다. 이 방향이 존재하기 때문에 bias라는 값을 구해 구해진 이동량에 더해주어 왼쪽이동(음수), 오른쪽 이동(양수)를 모두 양수처럼 다룹니다. bias는 이 지수부에 할당된 비트인 11비트를 반으로 나눠 -1을 수행해 구합니다. 2^(11-1) - 1 =1023. 이 bias값보다 크면 1.xx에서 점을 오른쪽으로 이동하고, bias값보다 작으면 왼쪽으로 이동합니다.
가수부는 1.xx로 수를 변환했을 때 xx부분을 그대로 넣습니다.
bias라는 방식 대신 첫번째 자리를 부호 비트로 하고 나머지를 이동량으로 하면 안되나 생각이 들었는데요. 크기 비교가 쉽기 때문에 bias방식을 사용한다고 합니다. 아무래도 -3, 3이고 지수부를 한 5비트라고 예시를 든다면 10011과 00011의 크기 비교는 부호 비교 -> 값 비교 2개의 과정이 필요하지만 bias 방식을 사용하게 되면, 15 + 3 = 10010, 15-3 = 01100 으로 비트 자체만 비교하여 대소를 한 번에 알 수 있습니다.
10진수 소수를 부동 소수점으로 변환하기
언젠가 유튜브를 보다가 0.1 + 0.2 === 0.3을 출력했을 때 false로 출력되는 것을 썸네일로 봤던 것 같은데요. 실제로 왜 이렇게 되는지 알아보겠습니다.
예시로 248.75라는 소수를 부동 소수점으로 바꿔보겠습니다. 10진수 소수를 부동 소수점으로 변환하기 위해서는 다음과 같은 절차를 거쳐 변환합니다.
1. 소수를 2진수로 변환한다.
2. 1.xx로 만드는 정규화를 수행한다.
3. 부동 소수점을 완성한다.
1번 과정부터 수행하겠습니다. -248.75를 2진수로 바꾸면 11111000.11이 됩니다. ( 소수점 부분은 10->2진수 소수점 변환을 사용합니다.)
다음 정규화 소수를 1.xx로 표현하기 위해 점을 어느 방향으로 얼마나 움직여야 하는지 찾습니다. 11111000.11에서는 왼쪽으로 7칸입니다. 지수부의 bias는 1023이므로 1023 + 7 = 1030 -> 10000000110(2) 이 됩니다.
정규화 이후 1.xx의 xx부분은 111100011이 되며, 최종적으로는 1/10000000110/11110001100....이 -245.75의 부동 소수점 값입니다.
부동 소수점의 표현 범위
지수부는 11비트며, 가능한 최대 값은 2^(11-1) - 1 = 1023입니다. 즉 1023번의 소수점 이동을 할 수 있다는 뜻입니다. 그리고 1.xx의 정규화 형태에서 앞의 1을 합치면 약 2^1024까지 표현할 수 있게 됩니다.
고정 소수점과의 비교를 위해 단정밀도 부동 소수점의 범위를 구하자면 1/8/23이므로 지수부의 최대는 2^(8-1) - 1 => 127이며, 앞의 1까지 합친다면 약 2^128입니다. 같은 32비트 단정밀도 고정소수점의 최대 값은 약 2^15로 표현할 수 있는 값의 크기 차이가 굉장히 큽니다.
javascript에서는..
멀리 돌아왔는데, 이제 0.1 + 0.2 === 0.3이 왜 false가 출력되는지 알 수 있습니다.
js는 배정밀도 부동 소수점을 사용하기 때문이며, 더 구체적으로는 소수점 아래 부분을 2진수로 변환하는 과정이 정확한 값으로 매칭되는게 아니라 근사치로 정해지기 때문에 0.1의 근사치, 0.2의 근사치, 0.3에 근사치가 모두 있으며 이 값이 정확하기 일치하지 못하기 때문에 false가 출력되죠.
'javascript' 카테고리의 다른 글
[JS] javascript에서 실행 컨텍스트의 Variable Environment, Lexical Environment 에 대한 탐구 (1) | 2025.01.24 |
---|---|
[Javascript] OR, nullish 연산자 (0) | 2025.01.07 |
[Javascript] for of, for in 문법에 대하여 (0) | 2025.01.06 |