0.1 + 0.2 === 0.3 ?

MaxlChan·2020년 8월 22일
12
post-custom-banner

자바스크립트에서 0.1 + 0.2는 0.3일까?

해당 내용에 대해 깊게 다루는 수많은 블로그와 각종 답변들이 많지만,
자료만 겁내 많고, 솔직히 이해하기가 정말 정말 어려웠다..ㅎ(문과를 나온 것을 후회하지 않지만 후회한다.)
나는 내가 이해한 방식대로 간단하게 정리해보고자한다.

🚨 올바르지 않은 내용이 있을 경우 댓글로 남겨주시면 감사드리겠습니다.

결론

결론부터 말하자면 아니다.

크롬 콘솔에서 찍어보어보면 확인할 수 있다.

왜 그럴까?

짧게 말하자면

인간은 숫자를 표현하기 위해 혹은 연산을 하기위해 10진법을 사용하고.
컴퓨터2진법을 사용하기 때문에

그 차이에서 비롯된 오차 결과이다.

컴퓨터는 모든 것을 전기 신호로 처리한다.
01이라는 두 가지 형태만 이해하고 사용할 수 있다.
(0은 전기신호 off, 1은 전기신호 on)

이러한 최소 단위를 컴퓨터에서는 bit라 한다.

비트(bit) - 컴퓨터에서 사용하는 가장 작은 데이터 단위, 하나의 비트는 2진수 1 또는 0으로 표현되어
데이터를 처리, 저장, 전송 할 때 사용된다.

출처: https://mindnet.tistory.com/entry/네트워크-이해하기-1편-Bit-와-Byte-차이점 [Mind Net]

즉, 우리(인간)가 14(십진법)이라는 숫자를 컴퓨터에 입력하면
컴퓨터에서는 내부적으로 해당 숫자를 1110(이진법)으로 변환하여 처리하고,

소수의 경우 0.75를 입력하면
컴퓨터에서는 0.11(이진법)으로 변환하여 처리한다.

문자도 마찬가지로 컴퓨터에서는 이진법으로 변환 처리하는데
ASCII 코드가 (영문 알파벳을 사용하는 대표적인 문자 인코딩)가 그 예시이다.

그럼 왜 2진수로 표현하는데 오차가 생기는 것인가에 대한 물음이 생길 것이다.

정수는 문제가 없다. 하지만 실수가 문제이다.

예를 들어, 십진법에서 1 나누기 3은
0.33333333333333333... 인 무한 소수로 정확하게 떨어지지 않는다.

이와 같이 0.1(십진법)을 이진법으로 변환하게 되면 (혹시 몰라 링크를.. 10진수을 2진수 변환하는 법)
0.000110011001100110011001100110011001...와 같이 무한 소수로 정확하게 떨어지지 않는다.

컴퓨터의 메모리는 무한하지 않다.
따라서 표현할 수 있는 최대한의 범위 내에서

컴퓨터는 가장 근접한 값, 즉 근사값으로 반올림 처리하여 우리에게 결과를 보여준다.
그리고 이로 인해 위와 같은 오차가 발생하는 것이다.

실수를 표현하기 위한 방식

실수를 표현하기 위한 방식으로는 크게 두가지가 있다.

1. 고정 소수점 방식

고정소수점은 소수점을 사용하여 고정된 자리수의 소수를 나타내는 것이다.
한정된 메모리에서 부동소수점 방식보다 좁은 범위의 수만 나타낼 수 있다.
출처 - 위키백과

말 그대로 소수부의 자릿수를 임의적으로 미리 정하여, 고정된 자릿수의 소수를 표현하는 것이다.

위 그림처럼, 실수를 표현하는데 총 32비트를 사용하고
부호는 1비트, 정수부는 15비트, 소수부는 16비트를 사용한다고 가정해보자.

그럼 14.1을 2진법 고정 소수점 방식으로 표현하게 되면

부호는 0(음수면 1이다), 14는 1110, 0.1은 0.00011001100110011001...이므로

0 000000000001110 0001100110011001 <= 이와 같이 내부적으로 저장된다.

고정 소수점 방식은 미리 소수부의 자릿수를 고정시키기 때문에

정수부를 표현하는 bit를 늘리면 큰 정수부 표현이 가능하지만,
세밀한 소수부 표현이 어려워지고

정수부를 표현하는 bit를 줄이면 세밀한 소수부 표현이 가능하지만,
큰 정수부를 표현하기 어렵다는 한계가 있다.

2. 부동 소수점 방식(Floating)

부동소수점(浮動小數點, floating point)방식은 실수를 컴퓨터상에서 근사하여 표현할 때
소수점 위치를 고정하지 않고 그 위치를 나타내는 수를 따로 적는 것으로,
유효숫자를 나타내는 가수(假數)와 소수점의 위치를 풀이하는 지수(指數)로 나누어 표현한다.
출처 - 위키백과

소수점 위치를 고정시키는 고정 소수점 방식과 달리
말 그대로 소수점 위치가 유동적인 방식이다. (Floating... 말 그대로 둥둥 떠다닌다..)

자바스크립트는 IEEE(전기 전자 기술자 협회)에서 개발한
IEEE-754라는 컴퓨터에서 부동소수점을 표현하는 가장 널리 쓰이는 표준에 의거하여
정수와 부동소수점을 표기한다.

(IEEE-754 표기법 중 32BIT를 사용하는 단정도와 64BIT를 사용하는 배정도방식이 존재하는데
자바스크립트는 배정도 방식을 사용한다고 한다.)

부동 소수점 방식은 부호(Sign), 지수부(Exponent), 가수부(Mantissa)로 나뉘고
32BIT 단정도에서 각각 부호는 1비트, 지수부는 8비트, 가수부는 23비트로 표현된다.
(64BIT 배정도에서 각각 부호는 1비트, 지수부는 11비트, 가수부는 52비트)

지수(Exponent)는 소수점 자리수를 계산을 위한 것, 가수부(Mantissa)는 실제 값을 나태내는 부분이라고 이해하면 된다.

그럼 -14.1을 2진법 부동 소수점 방식으로 표현해보자.

  • 우선 음수이므로 부호(Sign)1이다.

14.1 을 이진수로 변경하면 1110.00011001100110011001...이다.
지수부(Exponent)가수부(Mantissa) 를 구하기 위해서는 해당 값의 정규화과정을 거쳐야한다.

정규화란 2진수의 제일 왼쪽의 값이 1이 나오도록 소수점을 이동시키는 지수표현 방법을 말한다고한다.

1110.00011001100110011001... 정규화 과정을 거치면
->1.11000011001100110011001...*2³(소수점을 3번 앞으로 이동시켰다, 3은 지수 값).

  • 가수부(Mantissa)는 정규화 과정을 거친 값의 소수 부분 즉 11000011001100110011001...

정규화되어진 지수값 3매직넘버 127을 더한 값 10진수 130를 구한다.
(매직넘버는 지수부에 음수가 들어가지 않게 하기위해 더해주는 수로서, 구하는 공식이 있는 것 같으나..
이해도 잘 안되고, 여기서 핵심 부분이 아니기 때문에 그냥 넘어가기로 했다.. 참고로 64BIT배정도 방식에서는 매직넘버가 1023이다.)

  • 그 후 130을 2진수로 변환하여 얻은 값 10000010Exponent이다.

그럼 최종적으로 -14.1이란 10진법 숫자는

1 10000010 11000011001100110011001 <= 이와 같이 부동 소수점 방식으로 변환되어 내부적으로 저장된다.

오차를 눈으로 확인해보기

오차를 확인해보기 위해 위에서 부동 소수점 방식으로 변환된 2진수 값을 다시 10진수로 변환해보자.

// -14.1을 변환 
// 먼저 가수부의 값을 가져와서 생략되어진 1을 추가함. 그리고 10진수로 변경. 
var mantissa = parseInt('1' + '11000011001100110011001', 2); // 13421773
var exponent = 3; // 정규화 과정에서 계산된 지수 값
    
// 그리고, 1.11000011001100110011001 이 아닌
// 11000011001100110011001을 연산했으므로 -23의 지수가 추가됨. 
exponent += -23; 
    
var result = -mantissa * Math.pow(2, exponent); 
 
// -14.09999942779541 의 결과값. 

참고 - Dolen's blog

위에서 볼 수 있다시피 우리가 기입한 것은 -14.1이지만
내부 연산 과정을 통해서는 -14.09999942779541가 도출된다.

부동 소수점 방식을 사용하면 고정 소수점 방식보다 훨씬 더 많은 범위까지 표현할 수 있다.
하지만 이렇게 부동 소수점 방식에 의한 실수의 표현은 항상 오차가 존재한다는 단점을 가지고 있다.

결론적으로 0.1 + 0.2 !== 0.3 인 이유는
실수를 이진 부동소수점으로 처리하는 자바스크립트의 특성으로 인해 발생한 오차라고 받아들이면 되겠다.

참고

profile
한가지를 알아도 제대로 알자
post-custom-banner

2개의 댓글

comment-user-thumbnail
2020년 8월 29일

와 정리 넘 잘하셨네요..고생해서 정리하시는 만큼 보는사람이 읽기가 편한 것 같네요. 배워갑니다!

1개의 답글