JavaScript에서 부동소수점은 숫자를 표현하기 위해 IEEE 754 표준의 64비트 부동소수점 형식을 사용합니다.
일부 숫자를 정확하게 표현하지 못하고 오차가 발생할 수 있습니다.
부동소수점의 문제점
- 이진수 표현의 한계: 일부 십진수 소수는 이진수로 정확하게 표현될 수 없습니다. 예를 들어, 0.1이나 0.2와 같은 숫자는 이진 부동소수점으로 표현할 때 무한소수가 되어 근사치로 저장됩니다.
console.log(0.1 + 0.2); // 예상: 0.3, 실제: 0.30000000000000004
- 정밀도 손실: 계산 과정에서 작은 오차들이 누적되어 큰 오차로 이어질 수 있습니다. 이는 특히 반복 계산이나 금융 계산에서 문제가 됩니다.
해결책
- toFixed() 메서드 사용
toFixed() 메서드는 숫자를 지정된 소수점 자리까지 반올림하여 문자열로 반환합니다.
let sum = 0.1 + 0.2;
console.log(sum.toFixed(2)); // "0.30"
주의사항: toFixed()는 문자열을 반환하므로, 추가 계산이 필요하면 parseFloat()나 Number()로 숫자로 변환해야 합니다.
- 소수점 자리수를 고려한 계산
모든 숫자를 정수로 변환하여 계산하고, 결과를 다시 소수로 변환합니다.
let a = 0.1;
let b = 0.2;
let sum = (a * 100) + (b * 100); // 10 + 20 = 30
sum = sum / 100; // 0.3
console.log(sum); // 0.3
장점: 부동소수점 오차를 피할 수 있습니다.
단점: 처리해야 할 숫자의 범위에 따라 스케일링 팩터를 관리해야 합니다.
- EPSILON을 사용한 비교
두 부동소수점 숫자가 거의 같은지 비교할 때 Number.EPSILON을 사용합니다.
function areAlmostEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log(areAlmostEqual(0.1 + 0.2, 0.3)); // true
- 외부 라이브러리 사용
- Decimal.js
const Decimal = require('decimal.js');
let a = new Decimal(0.1);
let b = new Decimal(0.2);
let sum = a.plus(b);
console.log(sum.toString()); // "0.3"
- BigNumber.js
const BigNumber = require('bignumber.js');
let a = new BigNumber(0.1);
let b = new BigNumber(0.2);
let sum = a.plus(b);
console.log(sum.toString()); // "0.3"
장점: 매우 높은 정밀도의 계산이 가능합니다.
단점: 기본 숫자 타입보다 느리고, 코드가 복잡해질 수 있습니다.