본문으로 바로가기

소수, 실수 계산 오류, 부동소숫점

category JavaScript 2021. 4. 16. 19:42

알고리즘 문제를 푸는데 소숫점이 포함된 실수끼리의 계산을 해서 출력하는데

말도 안되는 값이 튀어나와서 너무 당황스러웠다

문제를 푸는 코드를 재현하는 것은 너무 귀찮기도하고, 인터넷에 가장 많이 떠도는 예시가

나의 상황과 유사했는데 그 코드를 재현하면 다음과 같다

    const float1 = 0.1;
    const float2 = 0.2;

    console.log(float1+float2);

이때 로그에 찍히는 결과값은 누구나 예상할 수 있듯 0.3이되어야한다

하지만 실제로 출력되는 값을 보면 0.30000000000000004이 나오는 것을 볼 수있다

왜 이런걸까?

고정소숫점 그리고 부동소숫점

고정소숫점

모두들 알고 있는 사실이겠지만 컴퓨터는 2진수로 모든 것을 처리한다

하나의 실수를 표현하기 위해서 컴퓨터가 사용하는 크기는 32비트로

음수, 양수를 표현하기 위한 부호에 1비트를 사용하고 나머지 31비트 갈라서 정수와 실수로 표현하는데 트레이드 오프 상태에 있다

즉, 수를 크게 표현하고 싶으면 정수에 사이즈를 늘리면되고, 수를 정교하게 표현하고 싶으면 소숫점이하 부분에 사이즈를 할당하면된다

123.2같이 우리가 평소에 사용하는 이 방법이 고정소숫점이다

10진수의 경우에는 위처럼 나타내지만 2진수로는 어떻게 실수를 표현할까?

4.625를 2진수로 표현해보자 정수 부분인 4는 원래 하듯이 100으로 바꾸고 소숫점은

.625*2 => 1.25 에서 1을 빼고

0.25*2 => 0.5 에서 0을 빼고

0.5*2 => 1.0 에서 1을 빼면 변환이 끝난다 이제 빼내온 숫자들을 위에서부터 아래로 나열하면

100.101로 표현할 수 있는 것

부동소숫점

하지만 위에서 정수와 소수부분을 저울질 하는것이 마음에 들지 않아서 나온 방법이 바로 부동소숫점인데

학교다닐 때 배운 유효숫자?를 나타낼 때 한 것 처럼 1.232 x 10^2 과 같이 나타낸다 소숫점의 자리가 고정되어있지 않고 움직인다는 뜻에서 부동 소숫점이라고 한다

위에서 살펴본 이진수도 마찬가지로 1.00101 x 2^2로 표현할 수 있다

그래서 왜 문젠데?

이렇게 부동소숫점 형식으로 이진수를 표현할 경우 이진수로 깔끔하게 표현할 수 있다면 문제가 없지만

그렇지 않을 경우 즉, 1/2의 거듭제곱으로 나누어떨어지지 않을 경우에는 우리가 원하는 수를 정확하게 나타낼 수가 없어서 오차가 생기게된다

예를 들어 0.8을 이진수로 나타낸다면

0.8 * 2 = 1.6 ... 1
0.6 * 2 = 1.2 ... 1
0.2 * 2 = 0.4 ... 0
0.4 * 2 = 0.8 ... 0
0.8 * 2 = 1.6 ... 1
0.6 * 2 = 1.2 ... 1
0.2 * 2 = 0.4 ... 0
0.4 * 2 = 0.8 ... 0
0.8 * 2 = 1.6 ... 1
0.6 * 2 = 1.2 ... 1
0.2 * 2 = 0.4 ... 0
0.4 * 2 = 0.8 ... 0
...
...
...
무한반복되게 돼서 우리가 원하는 결과를 얻을 수 없게된다

'JavaScript' 카테고리의 다른 글

IntersectionObserve API, 무한 스크롤  (0) 2021.05.15
IntersectionObserve API, 레이지 로딩  (0) 2021.05.13
Vanilla JS로 MVC 패턴 구현하기 1  (0) 2021.03.04
async/await  (0) 2021.02.07
Tagged templates  (0) 2021.01.23