[C++] 실수 자료형에 대한 고찰

Seungbo Shim·2024년 4월 16일
0

Algorithm

목록 보기
10/10
post-thumbnail
post-custom-banner

본 아티클은 바킹독 실전 알고리즘 0x01강 수강 후 회고를 위해 작성한 글입니다.

float, double

float, double이 C++에서 실수를 저장하기 위한 자료형인 건 인하대 하텍에서 24학번 과잠 입은 아무나 붙잡고 물어봐도 아는 내용이다.
그런데, 나는 지금껏 몇 년동안 C++의 자료형에 대해 그냥... 그냥 있으니까 사용하는 정도였지 뭘 더 깊이 파고들려 하지는 않았다. 그냥... 문제에서 실수를 출력하라니까 double을 쓰자 정도?
그러던 어느 날 이불 속에서 유튜브나 보던 중에 코딩애플 예전 영상이 떠서 봤고, 같은 내용을 이번 바킹독 강의에서 마주쳐 한 번 정리해 보기로 했다.

float과 double의 차이는 유효숫자를 저장할 수 있는 비트의 갯수다.
간단히, double이 float보다 더 많은 소수점 아래 숫자를 저장할 수 있다 정도로 바꿔 말할 수 있다. 이 내용 또한 다들 학부 1학년때 배웠을 내용이다.
더 자세히 들어가서 실수가 실제로 들어가는 포맷을 확인한다면...

IEEE-754 format

float

double

float과 double은 각각 위 형식에 저장된다.
sign 은 해당 실수의 음수/양수 여부,
exponent 는 실제 숫자를 유효숫자로 변환하는 과정에서 나온 지수,
fraction(mantissa)유효숫자를 저장하는 공간이다.

그렇다면 -6.75 라는 실수는 어떻게 저장될까?
-6.75 는 실수형으로 나타낼 시 다음과 같다.

-6.75 = -1.1011(2) * 2^2

해당 실수는 음수이므로 sign = 1,
지수가 2^2 인데, exponent 에는 2+127=129, 이 129를 2진수로 치환한 exponent = 10000001이 저장된다. 음수 지수까지 표현을 하기 위해 양수 지수는 127을 더해준다.
마지막으로 유효숫자인 fraction = 11011000000.... 을 저장해준다.

float과 double 형식에 -6.75를 저장하면 각각 위와 같이 저장된다.

반면, 0.1은 이진수로 변환한다면 순환소수가 된다.

0.1 = 0.00011001100110011....

따라서 float이나 double 형으로 저장하여도, 실제로는 0.1이 아닌 0.1의 아주 근사한 값만 저장되는 것이다.
이 때문에 다음의 현상이 발생한다.

#include <iostream>
using namespace std;

// IEEE-754 format 이란?

int main(void) {
    if (0.1+0.1+0.1 == 0.3) {
        cout << "true\n";
    } else {
        cout << "false\n";
    }
}

출력 :
false

0.1에는 실제로 0.1에 근접한 값이 저장되기 때문에, 0.1+0.1+0.1 이 0.3이 아니게 되므로 false를 출력한다.

0.1+0.1+0.1 == 0.3 이라는 자명한 결과를 실제로 나타내기 위해서는, 둘의 차이가 아주 작은 실수보다도 작다면 같은 수라고 출력하면 된다.

#include <iostream>
using namespace std;

int main(void) {
    if (0.1+0.1+0.1 == 0.3) {
        cout << "true\n";
    } else {
        cout << "false\n";
    }

    if (abs(0.1+0.1+0.1 - 0.3) < 1e-12) {
        cout << "true\n";
    } else {
        cout << "false\n";
    }
}

출력 :
false
true

더 좋은 방법은?

#include <iostream>
using namespace std;

// IEEE-754 format 이란?

int main(void) {
    if (0.1+0.1+0.1 == 0.3) {
        cout << "true\n";
    } else {
        cout << "false\n";
    }

    if (abs(0.1+0.1+0.1 - 0.3) < 1e-12) {
        cout << "true\n";
    } else {
        cout << "false\n";
    }

    if ((1+1+1)/10 == 3/10) {
        cout << "true\n";
    } else {
        cout << "false\n";
    }
}

출력 :
false
true
true

이렇게 실수형이 아닌 정수형으로 계산 후 나누면,
0.1이라는 실수를 마주할 필요 없어지니 오차가 발생하지 않는다.

profile
그래요 나 지금 거렁뱅이에요
post-custom-banner

0개의 댓글