ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 컴퓨터의 소숫점 문제
    컴퓨터구조 2022. 3. 1. 21:04
    • 기본적으로 구조상 소숫점 계산을 잘 못합니다. 이렇게 말씀드리면 어떤 분께서는 "엥 무슨소리야 절대 그렇지 않은데 당장 자바에서 혹은 내가 쓰는 언어에서는 정확하게 잘만되던데?"라고 생각하실 수 있습니다. 오늘은 이 이야기를 제가 알고 있는 선에서 자세히 풀어나가보자 합니다.
    • 우선 앞에서 얘기했던 "어떤 분"의 주장을 한번 더 자세히 들어보겠습니다. "자바dp 0.1 + 0.2 해보세요. 심지어 double 보다 더 적은 bit 수를 쓰는 float 를 써도 잘만됩니다. 보세요"
    • public class MyFloat {
       public static void main(String... args) {
         float a = 0.1f;
         float b = 0.2f;
         
         System.out.println("result = ", a+b)
      }
      }

      // CONSOLE
      // result = 0.3
    • 맞습니다. 결과가 0.3 이 나왔습니다. 하지만 다른 언어도 한번 살펴볼까요? 우선 파이썬 그다음 자바스크립트를 한번 살펴보겠습니다.
      a = 0.1
      b = 0.2
      console.log(`result = ${a+b}`)
      ​
      // CONSOLE
      // result = 0.30000000000000004
    • # PYTHON3

      a = 0.1
      b = 0.2
      print(f"result = {a+b}")

      # CONSOLE
      # result = 0.30000000000000004
    • 이렇게 된 이유는 무엇일까요? 자바는 위의 둘 언어보다 더 뛰어난 언어여서 더 높은 정확도를 지니는 걸까요? 오해를 하실 수도 있을거 같아 자바의 코드를 일부 변형해 보겠습니다.
    • public class MyFloat {
       public static void main(String... args) {
         float a = 0.1f;
         float b = 0.2f;
         
         System.out.println("float result = "+String.format("%.17f",a+b));
         
         double c = 0.1;
         double d = 0.2;
         
         System.out.println("double result = "+String.format("%.17f",c+d));
      }
      }

      // CONSOLE
      // float result = 0.30000001192092896
      // double result = 0.30000000000000004
    • 여기서 코드의 변경점은 double 변수(c, d)를 추가했다는 것이고 소수 17번째 수까지를 보고 싶어 String.format 을 사용했다는 차이점 밖에 없습니다. double 을 보면 파이썬, 자바, 자바스크립트 모두 동일하게 소수를 표현하고 있고 0.3을 제대로 표현하지 못한다는 점을 볼 수 있습니다. 왜 이런일이 벌어진 걸까요?

    왜 이런일이 벌어질까?

    • 기본적으로 컴퓨터는 실수를 표현하기 위해서 두가지 표기법을 사용합니다.
      • 고정 소수점 표기법(Fixed point notation)
      • 부동 소수점 표기법(Floating point notation)
    • 이 둘의 가장 큰 자이점은 소수점이 움직일 수 있는지 없는지로 판별할 수 있습니다.

    고정 소수점 표기법

    • 소수점이 고정되어 있는 실수 표현법입니다.
    • 32비트 실수를 고정 소수점 방식으로 표현하면 다음처럼 표현할 수 있습니다.

    • 고정 소수점은 부호, 정수부, 소수부를 표현하는 3부분으로 나뉘고 크기가 고정되어 있습니다.
    • 따라서 큰 수를 표현하거나 아주 작은 수를 표현하기에는 적합하지 않습니다.
    • 예를 들어 7.75를 2진수로 변환해 본다면 아래의 그림처럼 표현 할 수 있습니다.(편의상 16비트 체계로 사용했습니다.)
    • 하지만 보기에도 만약 128 이상이면 어떻게 할까요? 또는 1/512을 표현하려면 어떻게 해야할까요? 해당 방법은 범위가 제한적이다라는 것을 알 수 있습니다.
    • 따라서 잘 사용되지 않습니다.

    부동 소수점 표기법

    • 해당 방법은 실수를 부호부(sign), 가수부(Mantissa), 지수부(Exponent)로 나눕니다.
    • 부동 소수점 변환기 를 사용하면 좀 더 편하게 이해하실 수 있습니다.
    • 보통 언어에서 표현하는 실수 표현방법에 해당 방법이 사용됩니다. (Float, Double)
    • 기본 수식으로는 (-1)^S x M x 2^(E)로 표현할 수 있고 각 역할은 다음과 같습니다.
      • S: 부호부(Sign) 1비트를 의미하며 0이면 양수고, 1이면 음수가 됩니다.
      • M: 가수부(Mantissa) 23비트를 의미하며, 양의 정수로 표현합니다.
      • E: 지수부(Exponent) 8비트를 의미하며, 소수점 위치를 나타냅니다.
    • 만약 -12.34 를 부동 소수점 방식으로 표현해보면 아래와 같습니다.

    하지만 해당 방법도 표현할 수 있는 범위의 정확성에 한계가 있습니다.

    어떻게 해결 할 수 있을까?

    • 내부적으로 발생할 수 있는 작은 오차인 epsilon 값을 두어 해당 오차보다 아래인 경우 같다고 할 수 있습니다.
    • 해결책은 자바의 경우 BigDecimal 을 사용하는 것이고 파이썬의 경우는 Decimal 모듈을 사용하는 것입니다.
      • 공식문서에 보면 int[], char[] 배열을 활용하여 자리 끼리 연산하는것을 볼 수 있습니다.
      • 이 방법을 arbitrary precision 이라고 합니다.
        • 자리수 단위로 쪼개어 배열형태로 표현합니다.
        • Overflow가 발생하지 않습니다.
        • 속도보단 정확성이 더 중요한 경우 사용됩니다.
        • 상당히 느립니다. 부동 소수점 표기법의 경우 하드웨어로 구현되는 반면 해당 기술은 소프트웨어로 구현해야 되기 때문입니다.
        • 돈, 로켓, 암호학등
        • 파이썬의 경우 버전 3 부터 long 타입이 없어졌습니다.
        • 파이썬 2의 경우 intFixed Precision 이었고 longarbitrary precision 정수였기 때문에 달랐지만 파이썬 3에서는 intarbitrary precision 을 지원하고 fixed precision 은 더이상 지원되지 않습니다.
        import sys 
        ​
        a = sys.maxsize
        print(type(a))
        a = a+1
        print(type(a))
        ​
        # int
        # int

    '컴퓨터구조' 카테고리의 다른 글

    컴퓨터는 왜 2진법을 쓸까?  (0) 2020.05.28

    댓글