대부분 백엔드에서 계산해서 결과값을 넘겨주면 프론트에서 화면에 뿌려주는 편이다.
그런데 이번에 프론트에서 계산해서 적용해달라는 요구가 왔다.
(##.##) x 0.###### x #.##### x (##.## - ##.###) = "사용해야할 결과 값"
저런식의 계산인데 소수 계산이 아마무시했다.
과연 javascript에서 올바른 계산을 할 수 있을까..?
우선 javascript가 소수점 계산 오류가 나는 이유를 알아보자면,
1. 컴퓨터는 2진수(0,1)로 계산한다.
2. 우리가 사용하는 10진수를 2진수로 변환하면서 '무한소수'가 생성된다. ex) 0.30000000000000004
3. 컴퓨너 메모리의 한계까지 아주 근사한 값을 표시된 '무한소수'는 완전 일치가 아닌 근사값으로 계산 오류가 나타난다.
const a = 0.1;
const b = 0.2;
console.log('0.3이 아닌 무한소수 : ', a+b);
// Expected output : 0.30000000000000004
나는 평소에 toFixed를 잘 사용한다. (참고url : toFixed MDN)
소수점이 긴 값을 받으면 toFixed를 소수점을 줄였다.
그러나 그건 결과값을 받았을 때이고 사용한 것이라 이번 건에 사용하기 애매하다.
Math를 활용하라는 글도 있었는데 소수점을 버리거나, 반올림하는 것이 아니라서 이것도 애매.
이 계산만을 위해 라이브러리를 사용하는 것도 좋아보이지 않는다.
그래서 나는 소수점 만큼 곱해서 정수로 만들고 계산하기로 했다.
그러나 정수에서 방심할 수 없다.
왜냐하면 정수로 만들기 위해 소수점을 곱할 때 '무한소수'가 발생하기 때문이다.
console.log("무한소수 : ", 8.21*100);
// Expected output : 821.0000000000001
console.log("Math를 사용하자 : ", Math.round(8.21*100));
// Expected output : 821
이 때 Math.round를 활용해 깔끔하게 정수로 만든다.
이 방법으로 위험한 javscript 소수점 계산을 안전하게 한다.
// 소수점 자릿수를 계산 함수
getDecimalPlaces(num) {
var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
if (!match) { return 0; }
return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
},
// 소수점 정수로 변환 함수
convertToInteger(num) {
return { integer : Math.round(num * Math.pow(10, this.getDecimalPlaces(num))), pow : Math.pow(10, this.getDecimalPlaces(num))};
},
위 함수를 추가하여서 소수점 계산을 하였다.
그런데 Bito 플러그인을 활용해 ChatGPT에게 최적화 코드를 알려달라고 하면 그냥 parseFloat() 함수로 끝내버린다. (참고url : parseFloat MDN)
흐음..? 중요한 계산식이므로 최적화 안 하기로 했다.
(1)
toFixed MDN을 보다가 parseFloat()과 Number()에 대해 궁금해졌다.
둘 다 string을 number 타입으로 바꿀 수 있는 기능이 있다는 것은 알지만 뭐가 다른거지?
ChatGPT에 의하면, (결과 한 번 보고 테스트로 다시 확인했다.)
console.log("parseFloat :", parseFloat("3.14 meters"));
// Expected output: 3.14
console.log("Number :", Number("3.14 meters"));
// Expected output: NaN
console.log("parseFloat :", parseFloat("meters 3.14"));
// Expected output: NaN
parseFloat는 앞의 있는 숫자 string은 취급하고 뒤에 string은 버린다.
하지만 앞에 string이 있으면 NaN을 도출한다.
Number은 앞과 뒤 상관없이 모두 NaN!
평소에 Number()를 쓰는 편인데 parseFloat()의 기능을 사용하여 특별히 더 사용할 곳이 있는지 알아봐야겠다.
(2)
소수점 계산 오류는 javascript만의 문제가 아니다. (by ChatGPT)
(3)
소수점 계산 오류에서만 나타나는 것은 아니다. 정수 오류가 존재한다. (참고url : 멍청한 자바스크립트)
console.log("정수 오류 : ", 9999999999999999);
// Expected output : 10000000000000000
이 부분은 BigNumber 라이브러리같은 외부 라이브러리를 사용하거나 무시하라고 ChatGPT가 알려줬다.