[JavaScript] 실수 소수점 연산 오차

zmin·2022년 4월 22일
0
post-thumbnail

틀렸습니다.

요즘 자바스크립트에 좀 익숙해지고 싶어서 백준 문제를 풀어보고 있는데
오늘 문제는 아무리 봐도 나의 논리에 틀린게 없는데(ㅎㅎ) 자꾸 틀렸대서 뚜껑이 열릴 뻔함

실수 연산하기

C나 JAVA같이 자바스크립트는 변수를 선언할 때 타입을 따로 지정해주지 않는다(자바스크립트 공부하면서 이게 제일 어색했음)
그래서 숫자도 구분 없이 그냥 전부 64비트 부동소수점 형태로 저장(2진법)
사실 정수의 경우는 정확한 값으로 저장이 가능한데 부동소수점을 이용한 실수 표현은 2진법으로 변환할 때 작은 오차가 발생한다

이런 오차를 고려하지 못하고 계속 연산을 하게되면 정말 생각지도 못한 답을 얻게 된다.

백준 문제에서 제일 문제가 되었던 부분인데...

0.58 * 100이나 2900 / 50이나 똑같은게 아닌가 생각했는데 컴퓨터에게 전자는 '지금 소수 연산을 하라는거야????' 가 되는 거였다.

그리고 거기다 Math.floor()를 이용하게 되니까 더이상 사소한 오차가 아니게 됐음...틀렸습니다

이를 해결하는 가장 좋은 방법!

그냥 소수점 계산을 최대한 피하기

계산할 때 10의 거듭제곱을 곱해 정수로 만들어준 다음 계산하고 다시 10의 거듭제곱으로 나눠주는 과정을 거치는 것이 최선인 것 같다.

// 그냥 계산하기..
(0.1 * 10 + 0.2 * 10) / 10;		//0.3

또한 계산과정의 중간값에서 최대한 소수가 발생하지 않도록 계산 순서를 적절히 배치해주는 것이 중요할 것 같다

하지만 결국 소수가 피연산자로 들어가게 되면 오차가 또 발생할 수 있는데
이럴 때 사용할 수 있는 함수가 몇 가지 있다

// Math.round() / Math.floor() / Math.ceiling()
Math.round((0.1+0.2)*10)/10;	//0.3


//toFixed(n)
//소수점 아래 n자리까지 반올림하여 표시
(0.1+0.2).toFixed(1); 		//'0.3'

이도 완벽한 솔루션이라고 볼 수는 없는게 결과로 받을 수의 자릿수를 정해주어야 한다는 점이다..

하지만 컴퓨터 체제와 관련된 한계라 이것이 최선인 것 같음

ES6) Number.EPSILON

그래도 이런 문제를 보완하고자 ES6에서 Number.EPSILON이라는 것을 추가했는데
1보다 큰 수1과의 차이 중에서 가장 작은 값을 나타낸다.
Number객체의 정적 프로퍼티라서 인스턴스로 호출X
만약 Number.EPSILON보다 오차가 작다면 아예 없는 걸로 생각해도 된다 는 의미로 생각할 수 있다.

(0.1+0.2) - 0.3 < Number.EPSILON	//true

근데 이것도 결국 결과값이 뭔지 알고있어야...가능한 거 아닌가?


대충 알겠는데 뭔가...후련하진 않은 기분...

profile
308 Permanent Redirect

0개의 댓글