Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- #유니티
- Relational Database
- react
- 3티어 아키텍처
- 2티어 아키텍처
- javascript
- Factory 함수
- CSS
- npm
- 안정 정렬
- Private Blockchain
- #1인게임개발
- solidity
- Hybrid Blockchain
- CLI
- NoSQL
- 텍스트 가운데 정렬
- caverjs
- Factory Functions
- skip ci
- ES6 모듈
- UTXO
- http 모듈
- CSS Specificity
- 블록체인
- IP
- SQL
- short-circuiting
- 불안정 정렬
- 명시도
Archives
- Today
- Total
짹뚜 스튜디오
[Primitive Data Types] #3. 0.1 + 0.2 != 0.3 ?! 본문
해당 글은 모든 자바스크립트 개발자가 알아야 하는 33가지 개념에서 두 번째인 Primitive Data Types 항목을 공부하면서 간단하게 작성한 글이다.
자바스크립트에서 0.1 + 0.2를 해보면 0.3이 아니라 0.30000000000000004 가 나오는 것을 확인할 수 있다. 왜 이런 값이 나오는지 알아보려고 한다.
컴퓨터에서는 모든 값을 이진수로 저장을 한다. 자바스크립트의 number 타입들도 이진수로 저장이 되는데 실수를 이진수로 변환하는 데 사용하는 것이 IEEE 754 Floating Point Standard (부동소수점 방식) 이다. 아래와 같은 형식으로 이진수가 표현된다.
- Sign bit: 양수 (0) 와 음수 (1)를 1 bit로 표현한다.
- Exponent (지수): 11 bit로 이루어져있다.
- Mantissa or Fraction (가수): 52 bit로 이루어져 있다. 사실은 가수 부분의 가장 왼쪽에 bit 한 개는 항상 1이고 숨겨져 있다. 이것을 implict leading one이라고 한다. 그래서 숫자를 표현할 때는 53 bit를 사용하고 number의 범위가 -(2^53 -1) ~ 2^53 - 1 인 이유이다.
이렇게 해서 자바스크립트의 number 타입은 총 64 비트로 저장된다.
변환 방법
실수 12.25를 부동소수점으로 변환해보자.
- 실수가 양수이기 때문에 sign bit은 0이 된다.
- 먼저 정수 부분을 이진수로 나타내면 1100이 된다.
12 / 2 6, 나머지 = 0 6 / 2 3, 나머지 = 0 3 / 2 1, 나머지 = 1 1 / 2 0, 나머지 = 1 - 소수점 아랫부분을 이진수로 나타내면 01이 된다.
0.25 * 2 0.5, 0 0.5 * 2 1, 1 - 두 수를 합치면 1100.01 이 된다.
- 이 수를 정규화 즉 제일 왼쪽의 값이 1이 나오도록 소수점을 이동시키면 1.10001 * 2^(3) 이 된다. 하지만 여기서 바로 3을 11-bit 이진수로 바로 변경하지 않고 1023을 더해준다. 그 이유는 exponent가 음수와 양수 모두를 표현할 수 있어야 하기 때문이다. 그래서 11bit로 표현할 수 있는 최대 수인 2048의 절반인 1023을 더해준다.
- exponent는 3 + 1023 = 1026이 되고 이진수로 변경하면 10000000010 이 된다.
- mantissa는 정규화를 하고 나온 소수에서 소수점 아래 부분을 사용해서 10001 이 된다.
- 이제 모두를 다 합치면 0 100000000010 1000100000000000000000000000000000000000000000000000 가 된다.
부동소수점을 실수로 변환하는 공식은 다음과 같다.
Number = (-1)^(sign bit) * 2^(exponent -1023) * mantissa
- 위 예제에서 sign bit은 0 이기 때문에 (-1) ^ 0 = 1 이 된다.
- exponent bit 100000000010를 10진수로 변경하면 2^10 + 2^1 = 1024 + 2 = 1026이 되고 2^(1026-1023) = 2^(3) = 8 이 된다.
- mantissa bit 부분을 10진수로 변경할 때, 위에서 얘기했듯이 가장 왼쪽 비트 한 개는 1이고 숨겨져 있다고 했으니 (implict leading one) 같이 계산을 해줘야 한다: 1 1000100000000000000000000000000000000000000000000000. 그리고 여기는 소수점 아랫부분을 표현하기 때문에 자릿값은 왼쪽 bit부터 2^(0), 2^(-1), 2^(-2).... 가 된다. 따라서 2^0 + 2^(-1) + 2^(-5) = 1 + 0.5 + 0.03125 = 1.53125
- 이제 모두 곱해주면 1 * 8 * 1.53125 = 12.25 가 된다.
특별한 숫자
- 0: 모든 bit가 0으로 표현된다. implict leading one도 0이 된다. Sign bit이 0이면 +0, 1이면 -0가 된다.
- Infinity: Exponent 부분만 전부 1로 표현되고 (11111111111) 나머지는 모두 0이 된다. Sign bit이 0이면 +Infinity, 1이면 -Infinity가 된다.
- NaN: Exponent 부분만 전부 1로 표현되고 (11111111111), mantissa bit가 non-zero 면 NaN이 된다.
결론
0.1 + 0.2 가 0.30000000000000004 가 나오는 이유는 0.1을 이진수로 변환하면 0.0001100110011001100...... 수가 계속 반복하고 부동소수점은 64비트로 정해져 있기 때문에 0.1에 최대한 근사한 값으로 변환되면서 오차가 생긴다.
'개발 공부 > javascript' 카테고리의 다른 글
Type Coercion (타입 변환) (0) | 2021.12.19 |
---|---|
Value Types and Reference Types (0) | 2021.12.17 |
[Primitive Data Types] #2. Wrapper Object (래퍼 객체) (0) | 2021.12.15 |
[Primitive Data Types] #1. 무엇인가? (0) | 2021.12.15 |
브라우저에서 자바스크립트의 동작 원리: Call Stack (0) | 2021.12.14 |
Comments