JavaScript에서는 올림 ceil(), 버림 floor(), 반올림 toFixed(), toPrecision(), round() 등 다양한 방법으로 올림, 버림, 반올림을 제공합니다. 이글에서는 소수점에서 반올림하는 방법에 대해 자세히 알아보겠습니다. 오류가 있는 계산식과 가장 추천하는 방법을 말씀드리겠습니다.
* 스크립트 에디터가 없으신 분들은 온라인 에디터 codepen에서 테스트 해보시길 바랍니다.
목차
1. toFixed(), toPrecision()을 쓰면 안되는 이유
2. round() 함수의 문제점
3. round() 함수의 반올림 최적의 사용방법
1. toFixed(), toPrecision()을 쓰면 안되는 이유
toFixed와 toPrecision은 우선 return 값이 String입니다. 에디터에서 아래 코드를 작성해보면 console에 찍히는 값이 스트링임을 확인할 수 있습니다.
toFixed와 toPrecision은 대부분의 값을 정상 반올림하지만 일부 값에 대해 비정상적으로 출력합니다. 가장 유명한게 1.005지만 x.005 어떤 숫자를 넣어도 마찬가지입니다.
/* 반올림 오류 상황 - 정상은 1.01 이 나와야 함. */
var num = 1.005;
console.log ( num.toFixed(2) );
// 결과: "1.00"
console.log ( num.toPrecision(3) );
// 결과: "1.00"
정확히 어떤 동작에 의해서 그러는지는 모르지만 저게 금액 계산 로직에 들어있다고 생각하면 끔찍합니다. 낮은 확률이라도 오동작 할 위험이 있는 함수에 대한 사용법은 설명하지 않겠습니다. 그냥 쓰지맙시다.
* 이유는 부동소수점 오류로 10진수를 2진수 변환하면서 일부 숫자가 무한소수가 발생하기 때문이다.
* 부동소수점 오류를 확인하려면 console.log( 0.1 + 0.2 ); 를 찍어보자
2. round() 함수의 문제점
round() 함수는 return값이 숫자로 반올림에 많이 사용됩니다. round(12.34)와 같이 정수를 구하고자 소수점을 단순히 자를 때는 문제가 없지만 소수점 2자리, 3자리로 반올림 하기 위해 100, 1000을 곱하고 나누는 계산식에서는 문제가 있을 수 있습니다.
실제로 많은 분들이 소수점 2자리 반올림을 위해 Math.round(반올림할숫자 * 100) / 100를 많이 씁니다. 이는 아래와 같은 오류가 발생합니다.
/* 반올림 오류 상황 */
var num = 1.005;
console.log ( (Math.round(num*100)/100) );
// 결과: 1
console.log ( (Math.round(100.5)/100) );
// 결과: 1.01
위 코드를 보면 num*100 이나 100.5나 사실 같은 값인데 다른 결과가 나옵니다. 아이러니한 상황입니다. 이런 경우를 대비해 아주 작은 수인 입실론(EPSILON)을 더해주기도 합니다.
* 입실론은 2진수로 표현할 수 있는 0보다 큰 가장 작은 소수점 값입니다.
/* round() EPSILON */
var num = 1.005;
console.log ( Math.round((num+ Number.EPSILON) * 100) / 100 );
// 결과: 1.01
보통 위 조치로 대부분의 값이 나오기 떄문에 위처럼 쓰는 경우가 많습니다. 하지만 아무리 작은 숫자 EPSILON이라도 고유한 num 값에 무언갈 더한다는게 거슬립니다. 그리고 제가 예제를 찾지는 못했지만 이 경우도 잘못된 결과를 리턴하는 경우가 있다고 합니다. (예제를 아시는 분은 알려주세요 ㅠ)
3. round() 함수의 반올림 최적의 사용방법
제가 아는한 그리고 stackoverflow에서도 최적의 방법은 지수(e표기법)를 활용해서 e+10의제곱수와 같이 계산하는 것 입니다. 아마 곱하기나누기를 제거하는거 보면 스크립트에서 곱셈, 나눗셈의 동작 원리에서 오류가 있는 것 같습니다.
/* round() e+자리수 */
var num = 1.005;
console.log ( +(Math.round(num+"e+2")+"e-2") );
// 결과: 1.01
복잡해보이지만 텍스트형 숫자인 "1.005e+2" (1.005의 10의 2배수)로 만들어서 round하고 다시 "round결과e-2"로 만들어서 +로 숫자형태로 바꿔주는 것입니다.
* 정수의 경우도 9000경을 넘어가면 발생 가능
모두 정수로 변환 후 계산하는 방법도 있습니다.
'IT > Web' 카테고리의 다른 글
[JS] 자바스크립트 제곱과 루트 (0) | 2021.11.23 |
---|---|
[Web] jQuery ajax 기본 형태 (HTTP) (0) | 2021.11.19 |
[웹스퀘어5] 디자인 탭 깨질 때 CSS 적용 방법 (3) | 2021.11.09 |
[ES6] 자바스크립트 ES6 - 4. 정규표현식/정규식 (기초 및 예제) (0) | 2021.10.25 |
RGB 색상표 (0) | 2021.09.17 |
댓글