C++/Syntax 2021. 3. 9. 19:36

소수점 (Decimal Point)

고정 소수점 (정수)

  • 부호화 절대치

    • 맨 왼쪽 비트가 부호를 결정하고, 나머지 비트는 일반 양수를 다루듯 계산한다.
    000 -> +0
    001 -> +1
    010 -> +2
    011 -> +3
    100 -> -0
    101 -> -1
    110 -> -2
    111 -> -3
    • 안쓰는 이유

      • 순환성이 떨어진다.(+3 다음이 -0)

      • 보수를 사용하면 감산기 없이 가산기만으로 뺄셈이 가능하다.

  • 1의 보수

    • 비트를 반전시켜 순환성을 가지도록 했다.(+3 다음이 -3)

    • 덧셈기로 뺄셈이 가능하다.

    000 -> +0
    001 -> +1
    010 -> +2
    011 -> +3
    100 -> -3
    101 -> -2
    110 -> -1
    111 -> -0
  • 2의 보수

    • 1의 보수를 취하고 1을 더하는 형태이다.

    • -0을 없애고 숫자를 하나 더 챙겼다.

    • 빨리 만드는 방법

      • 오른쪽부터 왼쪽 방향으로 1을 만날 때까지 그대로, 그 이후는 반전시키면 된다.
    000 -> +0
    001 -> +1
    010 -> +2
    011 -> +3
    100 -> -4
    101 -> -3
    110 -> -2
    111 -> -1

부동 소수점(실수)

  • 부호 비트, 지수부, 가수부로 구성된다.

    • single-precision(float) : 지수부(8비트), 가수부(23비트)

    • double-precision(double) : 지수부(11비트), 가수부(52비트)

  • (-1)^(부호비트) * (1.(가수부)) * 2^(지수부 - 127)의 값을 가진다.

    • double-precision의 경우 127(0b01111111)이 아닌 1023(0b01111111111)

    • 참고

  • 출력 시 precision 조절하기

    double d(0.1);
    
    std::cout << std::setprecision(16);
    std::cout << d << std::endl;  // 0.10000000000000001
  • 오차는 부동 소수점의 한계이다.

    double d(0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1);
    
    std::cout << std::setprecision(16);
    std::cout << d << std::endl;  // 0.99999999999999989
  • 0으로 나누는 등 수학적인 오류가 발생할 가능성이 있는 경우

    • 실수를 0으로 나누면 inf이다.

    • 0을 0으로 나누면 nan이다.

    • <cmath> 라이브러리의 std::isinf, std::isnan으로 체크해야 한다.


float vs double

  • 의외로 연산 속도는 별 차이가 없다.

  • 메모리가 중요하다면 float, 정밀도가 중요하다면 double을 사용하는게 좋을 듯 하다.

  • 오차 예제

    #include <iostream>
    #include <iomanip>
    
    int     main()
    {
      using namespace std;
    
      const float f_begin = 1e9f;
      float f = f_begin;
      cout << setprecision(10000);
      cout << f << endl;    // 1000000000
      for (long long i = 0; i < 1e6; ++i)
        f += +1e0f;
      cout << f << "\n\n";  // 1000000000
    
      const double d_begin = 1e9f;
      double d = d_begin;
      cout << d << endl;    // 1000000000
      for (long long i = 0; i < 1e6; ++i)
        d += +1e0;
      cout << d << endl;    // 1001000000
    }
    • 근본적인 해결은 아니지만 임시로 해결하는 방법

      • 작은 연산은 따로 수행한 뒤 한번에 합치는 식으로 하면 된다.

        #include <iostream>
        #include <iomanip>
        
        int     main()
        {
            using namespace std;
        
            const float f_begin = 1e9f;
            float f = f_begin;
            cout << setprecision(10000);
            cout << f << endl;    // 1000000000
            float f2 = 0.0f;
            for (long long i = 0; i < 1e6; ++i)
                f2 += +1e0f;
            f += f2;
            cout << f << "\n\n";  // 1001000000
        }

'C++ > Syntax' 카테고리의 다른 글

C++ 형 변환 (Type Casting)  (0) 2021.03.09
C++ Boolean  (0) 2021.03.09
C++ 자료형 (Data Type)  (0) 2021.03.09
C++ 전처리기 (Preprocessor)  (0) 2021.03.09
C++ Namespace  (0) 2021.03.09