← 글 목록

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_VALUE
  • Number.MAX_SAFE_INTEGER / MIN_SAFE_INTEGER
  • Number.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 생략은 옛 엔진·팀 규칙 문제로 남는다. 항상 명시.