파크로그

서론

→ 왜 공부하게 되었는가?

  • Javascript 는 int , float 와 같은 다양한 숫자타입을 사용하는 것이 아닌 1 가지 숫자타입을 사용한다.
  • 이 숫자타입의 값은 배정밀도 64비트 부동소수점 을 따른다. 부동소수점 이라는 개념은 알지만 배정밀도 라는 언어는 낯설어 찾아서 공부하게 되었다.

 

왜이러는걸까요?

console.log(0.1+0.2) // 0.30000000000000004 console.log(0.1+0.2 === 0.3) // false

 

단정밀도? 배정밀도?

  • 단정밀도(single precision)는 32비트 를 사용하는 것을 이야기하며, 배정밀도(double precision)는 64비트를 사용하는 것을 이야기한다.

 

고정소수점? 부동소수점?

  • 7.625 를 2진수로 표현하고자 할 때, 변환하면 111.101 이 된다.

 

고정 소수점

  • . 을 기준으로 111 은 정수부가 되며, 101 은 소수부가 될 것이다.
  • 부호는 + 이므로 부호 비트는 0 이 된다. ( - 이면 1 )
  • 단정밀도 고정소수점 을 사용한다면 아래와 같다.0 | 000_0111 00000000 | 1010_0000 00000000
    • 단정밀도(32비트) 가 주어졌을 때, 정수부소수부 의 비트할당량은 정해져 있는가?
      • 정수부(15비트), 소수부(16비트)로 나눠져있다? NO
      • 고정 소수점은 특정 숫자의 소수점의 위치를 고정하는 방식으로 앞 10개의 비트는 정수 부분을 표현하도록, 나머지 22개의 비트는 소수점 이하의 부분을 표현하도록 할 수 있다.
      • 즉, 소수점 위치를 어디로 설정해야 하는지에 대해서는 정해진 답이 없으므로 사용자 임의대로 설정하면 된다.→ 0 | 000_0111 | 1010_0000 00000000 00000000
      • 단, 사용하는 수의 범위를 감안하여 오버플로우(Overflow) 가 발생하지 않도록 설정해야 한다.
    • 소수부 는 앞에서부터 채운다.

 

부동 소수점

  • 111.101정규화 하여 1.11101 * 2^2 로 나타낼 수 있다.
    • 2 진수를 1.xxxx..*2^n 꼴로 나타내는 것을 정규화
  • 단정밀도(32비트) 기준 맨 왼쪽 1 비트는 고정 소수점과 마찬가지로 부호 비트 이며, 이어지는 8 비트는 지수부, 나머지 23 비트는 가수부 로 표현된다.
    • 0 | 1000_0001 | 1110_1000 00000000 00000000 0000000
  • 배정밀도는 1 / 11 / 52 로 나뉘어진다.

 

바이어스(bias)

  • 부동 소수점의 지수부 를 보며 지수는 2^2 니까, 지수 2 를 2진수로 바꾸면 0000_0010 아닌가? 라고 생각이 들 수 있다.
  • IEEE 754 표준에 따르면, 지수를 그대로 넣는 게 아닌, 바이어스 (bias) 라고 하는 지정된 숫자를 더한 다음 넣어야 한다고 한다.
    • 32 비트에서 bias127 이며, 64 비트에서 bias1023 이다.
    • 위 사례는 127+2 = 129 이므로, 129 를 2진수로 표현한 1000_0001 이 나온 것이다.

bias 숫자?

  • 그렇다면 127, 1023 은 어디서 튀어나온 숫자일까?
  • C언어 에서 쓰이는 8비트 정수형 처리 키워드인 char 로 설명하면 char 는 8비트 변수이므로 -128~127 까지의 숫자를 취급할 수 있다.
    • 0000_0000 : 0
    • 0000_0001 : 1
    • ...
    • 0111_1111 : 127
    • 1000_0000 : -128
      • 이 부분이 왜 -128 인지 헷갈릴 수 있는데 일단은 이렇게 이해하고, 추후 포스팅하겠습니다.
    • 1000_0001 : -127
    • ...
    • 1111_1111 : -1
  • 바이어스 표현법 은 아래와 같다.
    • 0000_0000 : -127
    • 0000_0001 : -126
    • ...
    • 0111_1111 : 0
    • 1000_0000 : 1
    • ...
    • 1111_1111 : 128
  • 바이어스 표현법에 따르면 0 이 되는 수가 32비트 기준 127 이기 때문에 바이어스 상수가 127 이 된다.
  • 왜 바이어스 표현법을 쓰는가?
    • 지수 비트에도 부호 비트를 넣어서 음의 지수를 표현하는 것 보다 더 쉽게 구현할 수 있음

 

왜이러는걸까요? → Answer

  • 위 표현법에 따르면 0.1 은 부동 소수점 방식으로 제대로 표현하기 어렵다.
    • 비순환소수 의 문제로, 2 진수로 아무리 나누어도 무한소수가 되기 때문이다.
      • 0.1 * 2 → 0.2 : 0.0
      • 0.2 * 2 → 0.4 : 0.00
      • 0.4 * 2 → 0.8 : 0.000
      • 0.8 * 2 → 1.6 : 0.0001
      • 0.6 * 2 → 1.2 : 0.00011
      • 0.2 * 2 → 0.4 ... (무한 반복)
    • 따라서 0.1 을 제곱하여도 0.01 이 되지 않는다.
    const a = 0.1 a.toString(2) // "0.0001100110011001100110011001100110011001100110011001101"
  • 위에서 오는 부정확성 때문에 0.1+0.2 와 같은 문제가 생길 수 있다

 

참고

profile

파크로그

@파크park

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!