본문 바로가기

개발/Javascript

[Javascript] 0.1 + 0.2가 0.3이 아닌 이유 쉽게 이해하기

반응형

지난 글 복습하기

지난 포스팅에서 javascript에서 -0이 존재하는 이유에 대해 알아보았어요.

[양수 0, 음수 0 차이 쉽게 이해하기]

 

위의 글에서 이야기했던 -0이 존재하는 이유와 오늘 글의 주제와 관련이 있기 때문에

다시 한번 리캡해볼까요?

Recap!
- javascript는 숫자를 저장할 때 IEEE754표준에 따라서 64개의 비트(bit, 0 또는 1)를 사용한 부동소수점으로 저장한다.
- 이때 가장 앞에 오는 비트가 부호를 의미한다. 양수면 0, 음수면 1을 저장한다.
- +0을 저장할 때는 0을 저장하고 -0을 저장할 땐 1을 저장하기 때문에 차이가 발생합니다.

 

이 내용을 잘 이해해야 오늘의 이야기도 쉽게 이해할 수 있으니,

아직 잘 모르겠다 싶다면 지난 포스팅을 먼저 보고 오시는 것을 추천해요!


0.1 + 0.2 === 0.3 이 false라고요?

 

수학적으로 보았을 때 0.1 + 0.2 는 당연히 0.3입니다.

하지만 javascript 콘솔에 0.1 + 0.2 === 0.3을 입력하면 false라는 결과값을 출력합니다. 😨

 

0.1 + 0.2가 어떤 값이길래 0.3과 다르다고 하는 것일까요?


콘솔에 0.1 + 0.2를 찍어보니 0.30000000000000004라는 값을 출력합니다.

그렇다면 0.1 + 0.20.3보다 큰 값이라는 것이니,

0.1 + 0.2 > 0.3 을 찍어보니 true를 출력하네요. 

 

0.00000000000000004는 어디에서 온 값일까요? 🧐

 


원인은  부동소수점에 있다!

위키피디아 Double-precision_floating-point_format

별로 친숙하지 않은 이미지이지만, 다시 한번 들여다봅시다.

javascript는 숫자를 64bit를 사용한 부동소수점으로 형식으로 변환하여 저장합니다.

 

그렇다면 오늘 글의 주인공 0.10.2이 각각 어떻게 변환되는지 알아볼까요?

물론 우리의 목적은 "쉽게 이해하기"이기 때문에 직접 계산하지 않을 거예요. (저도 계산하는 방법 잘 기억 안 나요 ㅋㅋㅋ)

 

찾아보니 십진법을 [부동소수점으로 변환하는 사이트]가 있더라구요!

감사히 사용하겠습니다. 🥹👍🏻


부동소수점 변환 결과

0.1을 부동소수점으로 변환한 결과입니다.

0 01111111011 1001100110011001100110011001100110011001100110011010

 

여기부터 집중!

 

103으로 나누었을 때 3.333333이 반복되는 것처럼

0.1부동소수점으로 변환하는 과정에서 딱 떨어지지 않고, 11001100이 무한으로 반복되어요. 

무한반복되는 값을 64bit에 저장해야 하니 결국 ★반올림하게 되고,이때 오차가 발생합니다!

🔍 추가로 알아보기 : 왜 마지막 다섯 bit가 11010이 되었을까?

IEEE754 형식은 저장가능한 비트를 초과했을 때 반올림을 해요.
십진수에서 5 이상의 값은 올림을 하는 것처럼, 이진수에서는 1을 올림 합니다.

0.1의 부동소수점을 확인해 보겠습니다.
원래대로라면 11001100이 계속 반복되어야 하는데,
마지막 다섯 bit가 11001이고 그다음에 오는 수는 1입니다.

따라서 1은 올림처리하기 때문에 110011을 더한 11010이 되었어요!
즉, 0.1보다 약간 큰 숫자로 저장이 된 것이죠!

0.2를 부동소수점으로 변환한 결과입니다.

0 01111111100 1001100110011001100110011001100110011001100110011010

 

0.2도 마찬가지로 딱 떨어지지 않고 무한 반복되어 나머지 값을 반올림하여 저장합니다.


오차가 발생한 이유

무한 반복되는 값을 64bit에 저장하다 보니 어쩔 수 없이 오차가 발생하게 되었어요.

 

0.10.2는 저장하는 과정에서 올림이 발생했기 때문에,

0.1 + 0.2의 결과값은 0.3보다 약간 큰 0.30000000000000004라는 결과값이 나온 것이죠.

 

즉, 0.00000000000000004 반올림에 의해 발생한 오차값인 것입니다!


 

개발할 땐 어떻게 해야 할까?

const num1 = 0.1 const num2 = 0.2 console.log( Math.round((num1 + num2) * 10) / 10) // 0.3

소수를 사용하고 싶은 자릿수까지 정수로 만들어서 계산한 후, 

다시 소수로 변환하면 오차가 발생하는 현상을 방지할 수 있어요!


마치며

잊지 말아요

부동소수점은 언제 배웠는지 기억도 잘 안 나는데, 이렇게 다시 만나게 될 줄이야!

 

개발을 할 때에는 0.1이 정확하게 어떤 값으로 저장되고,

0.1 + 0.2의 결과값이 정확하게 어떤 값인지 알 필요는 없어요. 

 

하지만 컴퓨터는 숫자를 부동소수점으로 변환하여 저장하고

이 과정에서 오차가 발생할 수 있다는 것은 분명히 인지를 하고 있어야 합니다.

 

그렇지 않으면 예상하지 못한 오류가 발생할 수 있어요..😨


더 나아가기

0.1 + 0.2 === 0.3false인 것 처럼

분명히 true를 반환할 것 같은데, false를 반환하는 비교수식이 하나 더 있어요.


바로 NaN === NaN 입니다.

양쪽 항이 똑같은 모양을 하고 있는데 다르다니요 (???)

 

이 또한 IEEE754표준과 관련이 있습니다.

다음 글에서 알아보도록 합시다!

 


추천글

 

[Javascript] 양수0, 음수0 차이 쉽게 이해하기

+0과 -0 알아보기수학적으로 0은 음수와 양수의 사이에 차이가 없기 때문에 같은 값으로 봅니다.하지만 컴퓨터 세계에서는 `+0`과 `-0`은 엄밀히 말하면 다릅니다.  javascript 콘솔에서 `-0`을 입력하

shelly-log.tistory.com

 

반응형