짹뚜 스튜디오

[Primitive Data Types] #3. 0.1 + 0.2 != 0.3 ?! 본문

개발 공부/javascript

[Primitive Data Types] #3. 0.1 + 0.2 != 0.3 ?!

짹뚜 2021. 12. 16. 23:16

해당 글은 모든 자바스크립트 개발자가 알아야 하는 33가지 개념에서 두 번째인 Primitive Data Types 항목을 공부하면서 간단하게 작성한 글이다.

 

자바스크립트에서 0.1 + 0.2를 해보면 0.3이 아니라 0.30000000000000004 가 나오는 것을 확인할 수 있다. 왜 이런 값이 나오는지 알아보려고 한다.

 

컴퓨터에서는 모든 값을 이진수로 저장을 한다. 자바스크립트의 number 타입들도 이진수로 저장이 되는데 실수를 이진수로 변환하는 데 사용하는 것이 IEEE 754 Floating Point Standard (부동소수점 방식) 이다. 아래와 같은 형식으로 이진수가 표현된다.

  • Sign bit: 양수 (0) 와 음수 (1)를 1 bit로 표현한다.
  • Exponent (지수)11 bit로 이루어져있다.
  • Mantissa or Fraction (가수): 52 bit로 이루어져 있다. 사실은 가수 부분의 가장 왼쪽에 bit 한 개는 항상 1이고 숨겨져 있다. 이것을 implict leading one이라고 한다. 그래서 숫자를 표현할 때는 53 bit를 사용하고 number의 범위가 -(2^53 -1) ~ 2^53 - 1 인 이유이다.

이렇게 해서 자바스크립트의 number 타입은 총 64 비트로 저장된다.

변환 방법

실수 12.25를 부동소수점으로 변환해보자.

 

  1. 실수가 양수이기 때문에 sign bit은 0이 된다.
  2. 먼저 정수 부분을 이진수로 나타내면 1100이 된다.
    12 / 2 6, 나머지 = 0
    6 / 2 3, 나머지 = 0
    3 / 2 1, 나머지 = 1
    1 / 2 0, 나머지 = 1
  3. 소수점 아랫부분을 이진수로 나타내면 01이 된다.
    0.25 * 2 0.5, 0
    0.5 * 2 1, 1
  4. 두 수를 합치면 1100.01 이 된다.
  5. 이 수를 정규화 즉 제일 왼쪽의 값이 1이 나오도록 소수점을 이동시키면 1.10001 * 2^(3) 이 된다. 하지만 여기서 바로 3을 11-bit 이진수로 바로 변경하지 않고 1023을 더해준다. 그 이유는 exponent가 음수와 양수 모두를 표현할 수 있어야 하기 때문이다. 그래서 11bit로 표현할 수 있는 최대 수인 2048의 절반인 1023을 더해준다.
  6. exponent는 3 + 1023 = 1026이 되고 이진수로 변경하면 10000000010 이 된다.
  7. mantissa는 정규화를 하고 나온 소수에서 소수점 아래 부분을 사용해서 10001 이 된다.
  8. 이제 모두를 다 합치면 0 100000000010 1000100000000000000000000000000000000000000000000000 가 된다.

부동소수점을 실수로 변환하는 공식은 다음과 같다.

Number = (-1)^(sign bit) * 2^(exponent -1023) * mantissa

  1. 위 예제에서 sign bit은 0 이기 때문에 (-1) ^ 0 = 1 이 된다.
  2. exponent bit 100000000010를 10진수로 변경하면 2^10 + 2^1 = 1024 + 2 = 1026이 되고 2^(1026-1023) = 2^(3) = 8 이 된다.
  3. mantissa bit 부분을 10진수로 변경할 때, 위에서 얘기했듯이 가장 왼쪽 비트 한 개는 1이고 숨겨져 있다고 했으니 (implict leading one) 같이 계산을 해줘야 한다: 1 1000100000000000000000000000000000000000000000000000. 그리고 여기는 소수점 아랫부분을 표현하기 때문에 자릿값은 왼쪽 bit부터 2^(0), 2^(-1), 2^(-2).... 가 된다. 따라서 2^0 + 2^(-1) + 2^(-5) = 1 + 0.5 + 0.03125 = 1.53125
  4. 이제 모두 곱해주면 1 * 8 * 1.53125 = 12.25 가 된다.

특별한 숫자

  • 0: 모든 bit가 0으로 표현된다. implict leading one도 0이 된다. Sign bit이 0이면 +0, 1이면 -0가 된다.
  • Infinity: Exponent 부분만 전부 1로 표현되고 (11111111111) 나머지는 모두 0이 된다. Sign bit이 0이면 +Infinity, 1이면 -Infinity가 된다.
  • NaN: Exponent 부분만 전부 1로 표현되고 (11111111111), mantissa bit가 non-zero 면 NaN이 된다.

결론

0.1 + 0.2 가 0.30000000000000004 가 나오는 이유는 0.1을 이진수로 변환하면 0.0001100110011001100...... 수가 계속 반복하고 부동소수점은 64비트로 정해져 있기 때문에 0.1에 최대한 근사한 값으로 변환되면서 오차가 생긴다.

Comments