자바스크립트에서 콘솔창에 0.1 + 0.2를 입력해 소수점 연산을 하면, 0.3이 아니라 0.30000000000000004 가 나오는 걸 확인해볼 수 있다. 이렇게 소수점 계산 오류가 발생하는 이유와 어떤 방법으로 해결할 수 있는지에 대해 알아보려고 한다.
우리가 '10진법'을 사용하는 것과 다르게 컴퓨터는 계산을 할 때 0과 1만 사용하는 '2진법'을 사용한다.즉, 부동소수점 형식으로 처리한다.
10진법을 2진법으로 바꾸는 변환과정이 필요한데, 소수 중 일부는 이 과정에서 무한소수가 되어버린다. 하지만 컴퓨터 메모리에는 한계가 있어서 무한 소수를 다 담지 못하고 중간에 잘라서 유한 소수로 저장해버린다.
이 때, 부동소수점 계산에서 다음과 같은 오류가 발생한다.
정밀도 손실
이진수로 소수를 표현할 때, 소수점 이하 자릿수가 무한히 반복되는 경우가 발생할 수 있다. 하지만, 실제로는 유한한 자리수로 처리해야 하므로, 일부 자릿수는 생략되어 정밀도가 떨어진다.
반올림 오차
부동소수점 방식으로 연산을 처리할 때, 소수점 이하 자릿수를 반올림하여 처리하는데 반올림할 자릿수 이하의 값에 따라 결과가 달라질 수 있다. 따라서, 연산 결과에 오차가 발생하는 경우가 있다.
이때, 대소 비교에도 오차가 생길 수 있다.
toFixed()는 입력받은 숫자를, 매개변수만큼 자리수를 반올림해 String으로 반환해주는 함수이다.
다시말해 매개변수는 소수점 몇 번째 자리까지 나타낼 지를 나타낸다. 참고로 0부터 20까지 입력할 수 있다.
String으로 반환한하기 때문에 'number '타입으로 사용하고 싶다면 Number() 메서드를 한번 더 사용해서 숫자로 변환해줘야 한다.
let result = (0.1 + 0.2).toFixed(2);
// '0.30'
Number(result);
// 0.3
예를 들어 매개변수를 2로 지정해주면, 소수점 둘째자리까지 반올림해서 나타낸다. default값은 0이기 때문에 toFixed()만 입력하면 소수를 정수로 만들 수 있다.
Math 객체에 있는 Math.floor, Math.ceil, Math.trunc, Math.round 등의 메서드를 통해 소수 연산을 다룰 수도 있다.
이 중 Math.round는 '반올림'을 해주는 함수이다. 매개변수를 반올림한 후, 가장 가까운 정수 값을 반환한다.
toFixed()의 매개변수를 0으로 지정해줬을 때처럼 정수값을 반환해준다는 점에 주의하자.
Math.round(20.49);
// 20
Math.round((0.1 + 0.2) * 10) / 10;
// 0.3
Math.round((a + b) * 10) / 10 → 소수점 첫째 자리까지 반올림
Javascript의 수학 라이브러리를 이용하면 좀 더 쉽게 계산을 할 수도 있다. Big.js, BigNumber.js, Decimal.js, mathjs 등의 라이브러리가 있다.