|
| 1 | +# JavaScript에서 Number 타입의 문제점 |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +JavaScript의 `Number` 타입은 IEEE 754 부동 소수점 표준을 기반으로 동작하며, 정밀도 문제, 안전한 정수 범위, 연산 오류 등의 한계를 가지고 있다. 이를 이해하고 적절한 대처법을 적용하는 것이 중요하다. |
| 6 | + |
| 7 | +## Details |
| 8 | + |
| 9 | +### Number 타입 개요 |
| 10 | + |
| 11 | +- JavaScript에서 `Number` 타입은 **64비트 IEEE 754 배정도 부동 소수점 형식**을 사용한다. |
| 12 | +- 정수와 실수를 구분하지 않고 동일한 `Number` 타입으로 관리된다. |
| 13 | + |
| 14 | +```javascript |
| 15 | +console.log(typeof 10); // 'number' |
| 16 | +console.log(typeof 10.5); // 'number' |
| 17 | +``` |
| 18 | + |
| 19 | +### 정밀도 문제 |
| 20 | + |
| 21 | +- JavaScript의 `Number` 타입은 소수를 정확하게 표현하지 못하는 경우가 많다. |
| 22 | +- 이는 IEEE 754 부동 소수점 표준에서 **이진수 변환 시 발생하는 오차** 때문이다. |
| 23 | + |
| 24 | +```javascript |
| 25 | +console.log(0.1 + 0.2); // 0.30000000000000004 |
| 26 | +console.log(0.1 + 0.2 === 0.3); // false |
| 27 | +``` |
| 28 | + |
| 29 | +#### **왜 0.1 + 0.2가 0.3이 아닌가?** |
| 30 | +- 0.1과 0.2는 이진수로 정확하게 표현될 수 없는 값이다. |
| 31 | +- 실제 내부 저장 방식은 다음과 같다. |
| 32 | + - `0.1` ≈ `0.00011001100110011001100110011001100110011001100110011... (2진수)` |
| 33 | + - `0.2` ≈ `0.0011001100110011001100110011001100110011001100110011... (2진수)` |
| 34 | +- 이진수 연산 결과가 정확히 0.3이 되지 않기 때문에 오차가 발생한다. |
| 35 | + |
| 36 | +### 안전한 정수 범위 |
| 37 | + |
| 38 | +JavaScript의 `Number` 타입은 **안전한 정수(Safe Integer)** 를 다음 범위 내에서만 정확하게 표현할 수 있다. |
| 39 | + |
| 40 | + |
| 41 | + |
| 42 | +```javascript |
| 43 | +console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 |
| 44 | +console.log(Number.MIN_SAFE_INTEGER); // -9007199254740991 |
| 45 | +``` |
| 46 | + |
| 47 | +이 범위를 벗어나면 연산 결과가 부정확할 수 있다. |
| 48 | + |
| 49 | +```javascript |
| 50 | +console.log(9007199254740991 + 1); // 9007199254740992 |
| 51 | +console.log(9007199254740991 + 2); // 9007199254740992 (잘못된 결과) |
| 52 | +``` |
| 53 | + |
| 54 | +### 연산 오류 |
| 55 | + |
| 56 | +부동 소수점 연산에서 발생하는 오류 중 하나는 **0과 -0의 구분**이다. |
| 57 | + |
| 58 | +```javascript |
| 59 | +console.log(1 / 0); // Infinity |
| 60 | +console.log(1 / -0); // -Infinity |
| 61 | +``` |
| 62 | + |
| 63 | +`-0`과 `0`은 비교 연산에서는 같지만, 연산 결과는 다를 수 있다. |
| 64 | + |
| 65 | +### 해결 방법 |
| 66 | + |
| 67 | +#### **1. BigInt 사용** |
| 68 | +- `BigInt`는 정수를 안전하게 표현하는 별도의 데이터 타입이다. |
| 69 | +- `n`을 붙여서 `BigInt` 타입으로 변환할 수 있다. |
| 70 | + |
| 71 | +```javascript |
| 72 | +console.log(9007199254740991n + 2n); // 9007199254740993n |
| 73 | +``` |
| 74 | + |
| 75 | +- 단, `BigInt`는 `Number`와 혼합해서 사용할 수 없다. |
| 76 | + |
| 77 | +```javascript |
| 78 | +console.log(10n + 5); // TypeError: Cannot mix BigInt and other types |
| 79 | +``` |
| 80 | + |
| 81 | +#### **2. 라이브러리 활용** |
| 82 | +- 부동 소수점 오차를 방지하기 위해 `decimal.js`, `big.js`, `bignumber.js`와 같은 라이브러리를 사용할 수 있다. |
| 83 | + |
| 84 | +```javascript |
| 85 | +const Big = require('big.js'); |
| 86 | +console.log(new Big(0.1).plus(0.2).toString()); // "0.3" |
| 87 | +``` |
| 88 | + |
| 89 | +## Reference |
| 90 | + |
| 91 | +**issue**: Related issue in this repo |
| 92 | +- [JavaScript의 number는 왜 0과 -0을 지원하는가?](https://github.com/luke0408/TIL/issues/3) |
| 93 | + |
| 94 | +**author note**: Related note in this repo |
| 95 | +- [JavaScript - number](./Number.md) |
| 96 | +- [IEEE 754 Floating Point](../../../DevGeneral/IEEE/IEEE_754_Floating_Point.md) |
| 97 | + |
| 98 | +**link:** External reference |
| 99 | +- [MDN Web Docs: Number](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Number) |
| 100 | +- [ECMAScript Number 객체](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-number-objects) |
| 101 | +- [What Every Computer Scientist Should Know About Floating-Point Arithmetic (David Goldberg, 1991)](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) |
| 102 | +- [The Floating-Point Guide](https://floating-point-gui.de/) |
0 commit comments