notes
28장. Number
모던 JS Deep Dive — Number 객체·숫자 변환과 부동소수점 처리 정리.
참고: 이웅모 《모던 자바스크립트 Deep Dive》 28장 · [MDN Number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)
JS 숫자는 IEEE 754 배정밀도 부동소수 하나다. int/float 구분이 없다.
그래서 0.1 + 0.2 !== 0.3이 나오고, 큰 정수는 정확도를 잃는다. Number 장는 “숫자처럼 보이는 것”의 경계와 도구를 정리한다.
flowchart TB
N[Number] --> SAFE[안전 정수 범위]
N --> SPEC[NaN Infinity -0]
N --> METH[toFixed isFinite parse…]
BIG[BigInt] -.->|섞어 연산 금지| N
Number란
원시 숫자 타입의 래퍼 생성자이기도 하다. 일상은 리터럴·연산. new Number는 피한다(21).
const n = 1.5;
console.log(typeof n, n.toFixed(1)); // 일시 래퍼
프로퍼티
Number.EPSILON— 부동소수 비교 허용 오차Number.MAX_VALUE/MIN_VALUENumber.MAX_SAFE_INTEGER/MIN_SAFE_INTEGERNumber.NaN/POSITIVE_INFINITY/NEGATIVE_INFINITY
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(9007199254740993 === 9007199254740992); // true — 위험
메서드
Number.isNaN(NaN); // true
Number.isNaN('x'); // false — 전역 isNaN과 다름
Number.isFinite(1); // true
Number.isInteger(1.0); // true
Number.isSafeInteger(1e16); // false 쪽 체크
Number.parseInt('10px', 10); // 10
Number.parseFloat('1.2x'); // 1.2
인스턴스(원시 래퍼) 쪽: toFixed, toPrecision, toString(radix), toExponential.
console.log((0.1 + 0.2).toFixed(1)); // "0.3" 문자열
console.log(Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON);
형 변환
Number('01'); // 1
Number(''); // 0
Number(' '); // 0
Number(true); // 1
Number(null); // 0
Number(undefined); // NaN
+ '12'; // 12
parseInt는 앞에서부터 파싱, Number는 전체 엄격.
예제
function moneyEqual(a, b) {
return Math.round(a * 100) === Math.round(b * 100);
}
function toIntId(raw) {
const n = Number(raw);
if (!Number.isInteger(n) || !Number.isSafeInteger(n)) {
throw new Error('bad id');
}
return n;
}
헷갈리던 장면 / 함정
- 0.1+0.2
- 안전 정수 밖 카운터/ID
- isNaN vs Number.isNaN
- new Number(0) truthy
- BigInt와 Number 혼합 연산 TypeError
표
질문 → 자답
Q. 돈 계산은? A. 정수 센트·Decimal 라이브러리·정수 스케일. 부동소수 그대로 가산 금지에 가깝다.
Q. BigInt는? A. 임의 정밀도 정수. JSON·일부 Math와 별개. 필요 시 명시 변환.
웹 보강 메모
MDN Number와 floating-point guide. 레포에서 == 숫자 비교·전역 isNaN을 검색.
실무로 바로
- 비교: EPSILON 또는 정수 스케일
- 파싱: Number / parseInt(…,10)
- ID: safe integer 검증
- 표시: toFixed는 UI용
암묵적 변환 함정 모음
console.log(1 + '2'); // '12'
console.log(1 - '2'); // -1
console.log('' == 0); // true — 느슨한 비교 금지 이유
console.log(Number(undefined)); // NaN
console.log(Number(null)); // 0
정수 나누기·몫
const quot = Math.trunc(7 / 2); // 3
const floorDiv = Math.floor(7 / 2);
// 음수에서 trunc vs floor 차이
console.log(Math.trunc(-7 / 2), Math.floor(-7 / 2));
비트 연산은 32비트
~ / << 등은 ToInt32를 거친다. 큰 정수·플래그 마스크에 쓸 때 범위를 기억.
console.log(2 ** 31 >> 0);
표시와 로케일
console.log((1234567.89).toLocaleString('ko-KR'));
console.log((0.2).toLocaleString('ko-KR', { style: 'percent' }));
UI 문자열. 계산은 숫자로, 보여줄 때만 포맷.
실수 비교 유틸
function nearlyEqual(a, b, eps = Number.EPSILON * 4) {
return Math.abs(a - b) < eps;
}
스케일이 큰 수는 상대 오차가 낫다.
function nearlyRel(a, b, rel = 1e-12) {
return Math.abs(a - b) <= rel * Math.max(1, Math.abs(a), Math.abs(b));
}
문자열 숫자 가드
function asFiniteNumber(x) {
if (typeof x === 'string' && x.trim() === '') return null;
const n = typeof x === 'number' ? x : Number(x);
return Number.isFinite(n) ? n : null;
}
폼 입력 빈 문자열 → 0으로 가는 Number('') 함정을 막는다.
해시·ID에 숫자를 쓸 때
안전 정수 밖이면 문자열 ID·BigInt. JSON은 BigInt를 기본 직렬화 못 함 — 문자열로.
여기서 잠깐 💁♂️
29 Math — 삼각·난수·반올림 도구 상자.
보강 — -0과 Object.is
console.log(0 === -0); // true
console.log(Object.is(0, -0)); // false
console.log(Object.is(NaN, NaN)); // true
보강 — 지수·표기
1e3, 0xFF, 0b1010, 1_000_000 구분자. 가독성용.
실무 체크리스트 (Number)
- [ ] 돈·포인트는 정수 스케일 또는 Decimal
- [ ] ID는 safe integer 또는 문자열
- [ ] 입력 파싱은 Number/parseInt 중 정책 하나
- [ ] NaN 검사는 Number.isNaN
- [ ] UI 반올림은 toFixed, 계산과 분리
- [ ] BigInt 경계 JSON 직렬화 계획
예제 — 퍼센트·bps
function bpsToRatio(bps) {
return bps / 10000;
}
function applyBps(amount, bps) {
return amount * (1 + bpsToRatio(bps));
}
비율은 소수로, 표시는 퍼센트로. 혼용하지 않기.
오늘 지운 미신
“JS 숫자는 정수가 정확하다” → 안전 정수 안에서만. “toFixed는 숫자다” → 문자열. “isNaN만 있으면 된다” → 강제 변환 없는 Number.isNaN.
추가 예제 — 범위 매핑
function mapRange(x, inMin, inMax, outMin, outMax) {
const t = (x - inMin) / (inMax - inMin);
return outMin + t * (outMax - outMin);
}
console.log(mapRange(50, 0, 100, 0, 1));
센서값·슬라이더·캔버스 좌표 변환에 자주 쓴다. NaN/Infinite 가드는 호출 전에.
추가 — parseInt radix
parseInt('08', 10); // 8
parseInt('0x10', 16); // 16
parseInt('10', 2); // 2
radix 생략은 옛 엔진·팀 규칙 문제로 남는다. 항상 명시.