지난 글 복습하기
지난 포스팅에서 javascript에서 `-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.2`이 `0.3`보다 큰 값이라는 것이니,
`0.1 + 0.2 > 0.3` 을 찍어보니 `true`를 출력하네요.
0.00000000000000004는 어디에서 온 값일까요? 🧐
원인은 부동소수점에 있다!
별로 친숙하지 않은 이미지이지만, 다시 한번 들여다봅시다.
javascript는 숫자를 64bit를 사용한 부동소수점으로 형식으로 변환하여 저장합니다.
그렇다면 오늘 글의 주인공 `0.1`과 `0.2`이 각각 어떻게 변환되는지 알아볼까요?
물론 우리의 목적은 "쉽게 이해하기"이기 때문에 직접 계산하지 않을 거예요. (저도 계산하는 방법 잘 기억 안 나요 ㅋㅋㅋ)
찾아보니 십진법을 [부동소수점으로 변환하는 사이트]가 있더라구요!
감사히 사용하겠습니다. 🥹👍🏻
부동소수점 변환 결과
`0.1`을 부동소수점으로 변환한 결과입니다.
`0` `01111111011` `1001100110011001100110011001100110011001100110011010`
여기부터 집중!
`10`을 `3`으로 나누었을 때 `3.333333`이 반복되는 것처럼
`0.1`도 부동소수점으로 변환하는 과정에서 딱 떨어지지 않고, `11001100`이 무한으로 반복되어요.
무한반복되는 값을 64bit에 저장해야 하니 결국 ★반올림하게 되고,이때 오차가 발생합니다!
🔍 추가로 알아보기 : 왜 마지막 다섯 bit가 `11010`이 되었을까?
IEEE754 형식은 저장가능한 비트를 초과했을 때 반올림을 해요.
십진수에서 5 이상의 값은 올림을 하는 것처럼, 이진수에서는 1을 올림 합니다.
`0.1`의 부동소수점을 확인해 보겠습니다.
원래대로라면 `11001100`이 계속 반복되어야 하는데,
마지막 다섯 bit가 `11001`이고 그다음에 오는 수는 `1`입니다.
따라서 `1`은 올림처리하기 때문에 `11001`에 `1`을 더한 `11010`이 되었어요!
즉, `0.1`보다 약간 큰 숫자로 저장이 된 것이죠!
`0.2`를 부동소수점으로 변환한 결과입니다.
`0` `01111111100` `1001100110011001100110011001100110011001100110011010`
`0.2`도 마찬가지로 딱 떨어지지 않고 무한 반복되어 나머지 값을 반올림하여 저장합니다.
오차가 발생한 이유
무한 반복되는 값을 64bit에 저장하다 보니 어쩔 수 없이 오차가 발생하게 되었어요.
`0.1`과 `0.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.3`이 `false`인 것 처럼
분명히 `true`를 반환할 것 같은데, `false`를 반환하는 비교수식이 하나 더 있어요.
바로 `NaN === NaN` 입니다.
양쪽 항이 똑같은 모양을 하고 있는데 다르다니요 (???)
이 또한 IEEE754표준과 관련이 있습니다.
다음 글에서 알아보도록 합시다!
추천글
'개발 > Javascript' 카테고리의 다른 글
[Javascript] NaN 알아보기 (0) | 2024.07.02 |
---|---|
[Javascript] 양수0, 음수0 차이 쉽게 이해하기 (0) | 2024.06.23 |
[Javascript] 스크롤 이벤트로 비디오 재생 | 클론코딩 | 인터렉티브 웹 (0) | 2023.11.06 |
[Javascript] Hash 란? (0) | 2022.09.26 |
[토스페이먼츠 / Javascript] 자동 결제 연동하기 - SDK 결제창 이용 (0) | 2022.08.16 |