🐿️ 고정 소수점과 부동 소수점 (float, double)
컴퓨터에서 실수를 표현하는 방식을 크게 두 가지로 나눌 수 있다.
- 고정 소수점 (Fixed Point) : 소수점이 고정된 것
- 부동 소수점 (Floating Point) : 소수점이 움직임.
🐇 고정 소수점
예를 들어서 32Bits 중 절반 16개는 정수, 나머지 절반 16개는 소수점 부분의 숫자를 저장하는 것이다. 그렇기 때문에 비트 낭비가 있을 수 있다.
소수 부분에 많은 비트가 필요하지 않을 때, 정수 부분에 비트를 좀 더 쓸 수 있다면 메모리를 더 효율적으로 사용할 수 있을 것이다.
🐇 부동 소수점
C언어에서 사용하는 실수 자료형인 float과 double이 바로 IEEE754 표준에서 정의하는 부동 소수점을 따른다.
IEEE754을 따라서 부동 소수점이 저장될 때는 일단 수를 2진수로 변환하고 정규화식으로 표현하는데, 8.5 에 그 과정을 적용하면 아래와 같다.
 |
그리고 여기서 다음 세 가지를 뽑아내는데
- 부호 : 0 (+이면 0, -이면 1)
- 지수 : 130 (지수는 3이지만 IEEE754표준에서는 지수 + 127 을 저장하는데, 이 127을 bias라고 함, 이 bias를 더하는 이유는 지수가 음수일 때 연산을 쉽게 하기 위해서..)
- 가수 : 00010000000000000000000 (정규화 식에서 소수점 뒷부분. 즉, 모자라는 부분은 0으로 채움)
이 세 가지를 아래 그림과 같은 구조로 저장하여 사용한다.
 |
이렇게 비트를 좀 더 효율적으로 사용하여 더 많은 수를 표현 할 수 있다.
하지만 부동 소수점에는 다음과 같은 단점이 존재한다.
- 정밀도
- 성능
정밀도가 떨어지는 이유는 컴퓨터가 이진법으로 계산하기 때문인데 어떠한 소수들은 소수점이 무한대로 뻗어나가기 때문이다.
예를 들어 0.1은 이진 정규화식으로 표현하면 무한소수가 된다.
 |
보시다시피 오차를 확인할 수 있다.
C++에서는 기본적으로 이 부동 소수점을 채택하고 있으며 관련 자료형은 다음과 같다.
float(4Byte)은 유효숫자 6-7 까지 보장해준다
double(8Byte)은 유효숫자 15-16 까지 보장해준다
🐿️ 실수 출력하기
1️⃣ printf()로 출력
1
2
3
4
5
6
7
8
9
10
#include <stdio.h> //표준 입출력 헤더
int main(void)
{
double df = 3.4;
float f = 3.4f;
printf("df:%lf\n",df); // C95버전까지는 %f 사용했지만 C99로와서 명목상 %lf 사용.
printf(" f:%f\n",f);
return 0;
}
다음은 12.3456을 소숫점 이하 2자리 까지 출력한 예이다.
1
2
3
4
5
6
7
#include <stdio.h> //표준 입출력 헤더
int main(void)
{
printf("%.2f\n", 12.3456);
return 0;
}
실수 출력 포멧 %f%e%g
- %e : 지수 표기로 출력하는 것
1
2
3
4
5
6
7
#include <stdio.h> //표준 입출력 헤더
int main(void)
{
printf("%e\n", 12.3456);
return 0;
}
출력 결과
1
1.234560e+01
- %g : 가장 간단하게 출력할 수 있는 방법으로 출력. 이 때 소숫점 이하 자리도 값이 없으면 출력하지 않는다.
1
2
3
4
5
6
7
8
9
10
#include <stdio.h> //표준 입출력 헤더
int main(void)
{
printf("%g\n", 13.4); //13.4
printf("%g\n", 0.00000012); //1.2e-07
printf("%g\n", 1.300); // 1.3
printf("%f\n", 1.300); // 1.300000
return 0;
}
출력 결과
1
2
3
4
13.4
1.2e-07
1.3
1.300000
2️⃣ cout으로 출력
cout의 경우 변수에 맞게 자동으로 형을 결정하여 출력이 가능하다.
만약, 출력 방식을 선택 하고 싶으면 아래를 참고하면 된다.
cout으로 실수를 출력할 때, 만약 그 수가 큰 수라면 자동으로 지수 표기법으로 변경되는 문제가 생긴다. 이 때 정상적으로 출력 되도록 하려면 cout « fixed를 사용하면 된다.
cout << fixed
: 소수점을 고정시켜 사용cout.precision(n)
: n자리까지 소수점을 표현 (n + 1 자리에서 반올림)
아래처럼도 사용 가능
cout.setf(ios::fixed)
:cout << fixed
와 같은 기능cout.unsetf(ios::fixed)
: fixed를 해제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream> //표준 입출력 헤더
using namespace std;
int main(void)
{
ios::sync_with_stdio(false); // 입출력 스트림의 동기화 해제
cin.tie(); cout.tie(); // flush를 적게 하기 때문에 입출력 속도를 향상시킴
double num = 12345678.111111;
cout << num << endl; // 그냥 출력
cout << fixed; // fixed 설정
cout.precision(6); // 고정시킬 자리 설정
cout << num << endl;
cout.unsetf(ios::fixed); // fixed 해제
cout << num << endl;
cout.setf(ios::fixed); // fixed 설정
cout << num << endl;
}
출력 결과
1
2
3
4
1.23457e+07
12345678.111111
1.23457e+07
12345678.111111
🐿️ 소수점 올림, 내림, 반올림, 버림
🐇 헤더파일(header)
[C 언어] : math.h
[C++] : cmath
🐇 함수 설명
- ceil : 천장을 의미하며 올림
- floor : 바닥을 의미하며 내림
- round : 반올림 (C++11 부터 사용 가능)
- trunc : 버림 (C++11 부터 사용 가능)
🐇 함수 원형 및 사용법
- [C언어] : C언어는 함수 오버로딩을 지원하지 않으므로 double 타입으로만 존재합니다.
-
ceil (올림)
double ceil(double n);
-
floor (내림)
double floor(double n);
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<stdio.h>
#include<math.h>
int main(void){
//양수
double n1 = 4.2;
double n2 = 4.2;
double n3 = 4.2;
double n4 = 4.7;
//올림
printf("%0.1lf\n", ceil(n1));
//내림
printf("%0.1lf\n", floor(n2));
//반올림
printf("%0.1lf\n", floor(n3 + 0.5));
printf("%0.1lf\n", floor(n4 + 0.5));
return 0;
}
1
출력 결과
1
2
3
4
5.0
4.0
4.0
5.0
- [C++] : C++에서는 method 이름이 달라도 method를 선언할 수 있는 오버로딩을 지원합니다.
-
ceil (올림)
float ceil (float x);
double ceil (double x);
long double ceil (long double x);
-
floor (내림)
float floor (float x);
double floor (double x);
long double floor (long double x);
-
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<iostream>
#include<cmath>
using namespace std;
int main(void){
//음수
double n1 = -4.2;
float n2 = -4.2;
double n3 = -4.2;
float n4 = -4.7;
//소수점 출력.
cout.setf(ios::fixed, ios::floatfield);
//소수점 아래 1자리 까지
cout.precision(1);
//올림
cout << ceil(n1) << endl;
//내림
cout << floor(n2) << endl;
//반올림
cout << floor(n3 + 0.5) << endl;
cout << floor(n4 + 0.5) << endl;
return 0;
}
1
출력 결과
1
2
3
4
-4.0
-5.0
-4.0
-5.0
1
2
3
4
5
6
7
8
9
10
11
12
- round (반올림)
float round(float num);
double round(double num);
long double round(long double num);
double round(T x);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//C++ round example.
#include<iostream> //cout
#include<cmath> //round, ceil, floor
using namespace std;
int main(void)
{
double a1 = 3.2;
double a2 = 3.7;
double a3 = -3.2;
double a4 = -3.7;
cout << "round(3.2) : " << round(a1) << endl;
cout << "round(3.7) : " << round(a2) << endl;
cout << "round(-3.2) : " << round(a3) << endl;
cout << "round(-3.7) : " << round(a4) << endl;
cout << endl;
system("pause");
return 0;
}
1
출력 결과
1
2
3
4
round(3.2) : 3
round(3.7) : 4
round(-3.2) : -3
round(-3.7) : -4
1
2
3
4
5
6
7
8
9
10
11
12
- trunc (버림)
float trunc(float num);
double trunc(double num);
long double trunc(long double num);
double trunc(T num);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//C++ trunk example.
#include<iostream> //cout
#include<cmath> //round, ceil, floor, trunc
using namespace std;
int main(void)
{
double a1 = 3.2;
double a2 = 3.7;
double a3 = -3.2;
double a4 = -3.7;
cout << "trunc(3.2) : " << trunc(a1) << endl;
cout << "trunc(3.7) : " << trunc(a2) << endl;
cout << "trunc(-3.2) : " << trunc(a3) << endl;
cout << "trunc(-3.7) : " << trunc(a4) << endl;
cout << endl;
system("pause");
return 0;
}
1
출력 결과
1
2
3
4
trunc(3.2) : 3
trunc(3.7) : 3
trunc(-3.2) : -3
trunc(-3.7) : -3