숫자
자바스크립트의 숫자 타입은 number
가 유일하며 '정수', '부동 소수점 숫자'를 모두 아우른다. '정수'에 따옴표를 친 건, 다른 언어와 달리 자바스크립트 에는 진정한 정수가 없다는 이유로 오랫동안 욕을 먹었기 때문이다. 언젠가 개선될 날이 오긴 하겠지만. 일단 현재는 모든 숫자를 number
타입 하나로만 표시한다.
따라서 자바스크립트의 '정수'는 무동 소수점 값이 없는 값이다.(예: 42.0은 '정수' 42와 같다.)
사실상 모든 자바스크립트 언어를 통틀어 대부분의 현대 프로그래밍 언어는 'IEEE 75' 표준을 따른다. 자바스크립트 number
도 IEEE 754 표준을 따르며, 그중에서도 정확히는 '배 정도(Double Precision)'표준 포맷을 사용한다.
웹서핑을 하다보면 이진 부동 소수점 숫자가 메모리에 저장되는 방식과 그렇게 설계된 의미는 무엇인지 시시 콜콜 자세히 설명하, 좋은 참고자료가 많다. 자바스크립트 number
의 정확한 사용법을 이해하고 자 메모리 비트 패턴가지 알아야 할 필요는 없다.
숫자 구문
자바스크립트 숫자 리터럴은 다음과 같이 10진수 리터럴료 표시한다.
var a = 42;
var b = 42.3;
소수점 앞 정수가 0이면 생략 가능하다.
var a = 0.42;
var b = 0.42;
소수점 이하가 0일때도 생략 가능하다.
var a = 42.0;
var b = 42;
42.처럼 표기하는 건 일반적이지도 않고 다른 사람이 코드를 읽을 대 혼동을 일으킬 수 있으니 별로 좋은 생각은 아니다. 어쨌든 틀린 코드는 아니라는 것을 알아두자.
대부분의 숫자는 10진수가 디폴트고 소수점 이하 0은 뺀다.
var a = 42.3;
var b = 42.0;
a; // 42.3
b; // 42
아주 크거나 아주 작은 숫자는 지수형으로 표시하며, toExponential()
메서드의 결과값과 같다.
var a = 5e10;
a; // 50000000000
a.toExponential(); // "5e+10"
var b = a * a;
b; // 2.5e+21
var c = 1 / a;
c; // 2e-11
숫자 값은 number
객체 레퍼로 박싱할 수 있기 때문에 number.prototype
메서드로 접근할 수 도 있다. 예를들면 toFixed()
메서드는 지정한 소수점 이하 자리수 까지 숫자를 나타낸다.
var a = 42.59;
a.toFixed(0); // "43"
a.toFixed(1); // "42.6"
a.toFixed(2); // "42.59"
a.toFixed(3); // "42.590"
실제로는 숫자 값을 문자열 형태로 반환하며, 원래 값의 소수점 이하 숫자보다 더 많은 자릿수를 지정하면 그만큼 0이 우측에 붙는다. toPrecision()
도 기능은 비슷하지만 유효 숫자 개수를 지정할 수 있다.
var a = 42.59;
a.toPrecision(1); // "4e+1"
a.toPrecision(2); // "43"
a.toPrecision(3); // "42.6"
a.toPrecision(4); // "42.59"
a.toPrecision(5); // "42.590"
두 메서드는 숫자 리터럴에서 바로 접근할 수 있으므로 굳이 변수를 만들어 할당하지 않아도 된다. 하지만 .
이 소수점일 경우엔 프로퍼티 접근자가 아닌 숫자 리터럴의 일부로 해석되므로 .
연산자를 사용할 때는 조심하자.
// 잘못된 구문
42.toFixed(3); // SyntaxError
// 올바른 구문
(42).toFixed(3); // "42.000"
0.42.toFixed(3); // "0.420"
42..toFixed(3); // "42.000"
42.toFixed(3)
에서 구문 에러가 난 이유는 이 42.
리터럴의 이룹가 되어 버려 .toFixed
메서드에 접근할 수단이 없어지기 때문이다.
42..toFixed(3)
의 경우 첫번째 .
은 숫자 리터럴의 일부, 두 번째 .
은 프로퍼티 연산자로 해석되므로 문제가 없다. 하지만 보기에 어색하고 불편하므로 이런 코드는 잘 쓰지 않는다. 사실, 원시 값이 메서드를 직접 호출할 일은 거의 없다.
`number.prototype을 확장하여 숫자 값연산 기능을 추가한 라이브러리들도 있다 .하늘에서 10초동안 돈다발이 비처럼 쏟아지게 하는 10..makeItRain() 같은 코드가 있을지 모를일이다. 어쨌든 이런 라이브러리를 구해 써도 된다.
이런 코드 역시 올바른 구문이다.
42 .toFixed(3); // "42.000"
그러나 숫자 리터럴에 이런 코딩을 해놓으면 다른 개발자들(본인까지도) 안구를 메마르게 할 뿐이니 그러지 말자.
큰 숫자들은 보통 다음과 같이 지수형으로 표시한다.
var onethousand = 1e3; // 1 * 10^3
var onemilliononehundredthousand = 1.1e6; // 1.1 * 10^6
숫자 리터럴은 2진, 8진, 16진 등 다른 진법으로도 나타낼 수 있다.
ES6 + 엄격모드(strict mode)에서는 0363처럼 0을 앞에 붙여 8진수를 표시하지 못한다. 느슨한 모드에서는 과거 형식을 계속 쓸 수는 있지만 미래를 생각해서 쓰지 않는게 좋다.
ES6 부터는 다음과 같이 쓸 수 도 있다.
0o363; // 243의 8진수
0o363; // 위와 같음
0b11110011; // 243의 이진수
0b11110011; // 위와 같음
옆에 동료 개발자가 앉아 있다면, 소문자로 써달라고 부탁하자. 0
다음에 오는 O
는 쓸데없이 헷갈리니까 언제나 소문자로 표기하기 바란다.
작은 소수 값
다음은 널리 알려진 이진 부동 소수점 숫자의 부작용 문제다.
0.1 + 0.2 === 0.3; // false
수식만 보면 분명히 true
다. 그런데 결과는 false
가 나온다. 간단히 말하면, 이진 부동 소수점으로 나타낸 0.1과 0.2는 원래의 숫자와 일치하지 않는다. 그래서 둘을 더한 결과 역시 정확히 0.3이 아니다. 실제로는 0.3000000000000000004에 가깝지만, '가깝다'고 해도 '같은'것은 아니다.
그럼 자바스크립트가 모든 값을 정확하게 표시하도록 숫자 구현 방식을 바꾸어야 맞을까? 그렇게 생각하는 사람들도 있다. 그래서 지난 수십년 동안 갖가지 대안이 제시되었지만 어느 것도 채택되지 않았고 앞으로도 사정은 비슷할 것 같다. 누군가 손을 번쩍 들고 "제가 버그를 고쳤습니다!!"라고 쉽게 말할 수 있을 것 같지만 그리 간단치가 않다. 간단했다면 머리 좋은 사람들이 벌써 오래전에 다 풀어놨을 것이다.
그렇다면 한번 생각을 해보자. 숫자 값이 정확하리라는 보장이 없다면 숫자를 쓰지 말라는 것일까? 물론 그렇지 않다.
부동 소수점 숫자를 조심히 다루어야할 애플리케이션도 있겠지만, 많은 애플리케이션이 전체수 만들, 그것도 기껏해야 백만이나 조 단위 규모의 숫자를 다룬다. 이런 상황이라면 언제나 안심하고 자바스크립트의 숫자 연산 기능을 믿고 써도 된다.
그럼 0.1 + 0.2
와 0.3
두 숫자는 어떻게 비교해야 할까?
가장 일반적으로는 미세한 '반올림 오차'를 허용 공차로 처리하는 방법이 있다. 이러헥 미세한 오차를 '머신 일십론'이라고 하는데. 자바스크립트 숫자의 머신 입실론은 2^-52다.
정수인지 확인하기
ES6부터 number.isinteger()
로 어떤 값의 정수 여부를 확인한다.
Number.isInteger(42); // true
Number.isInteger(42.0); // true
Number.isInteger(42.3); // false
ES6 이전 버전을 위한 풀리필은 다음과 같다.
if (!Number.isInteger) {
Number.isInteger = function (num) {
return typeof num === "number" && num % 1 === 0;
};
}
'JAVASCRIPT' 카테고리의 다른 글
[JS] this 란? (0) | 2022.01.26 |
---|---|
[JS][값][마무리] 값 vs 레퍼런스 (0) | 2022.01.26 |
[JS][값] 특수 값 (0) | 2022.01.26 |
[JS][값] 문자열 (0) | 2022.01.24 |
[JS][값] Array (0) | 2022.01.24 |
댓글