클라이언트/ 서버/ 엔지니어 " 게임 개발자"를 향한 매일의 공부일지

데이터 처리 2 : 부동 소수점수와 C++ 산술 연산자 본문

프로그래밍 언어/C++

데이터 처리 2 : 부동 소수점수와 C++ 산술 연산자

huenuri 2024. 8. 25. 11:10

앞선 포스트에서 C++의 정수형에 대해서 살펴보았다. 이제 C++의 두번째 기본 데이터형인 부동 소수점형에 대해서 학습해볼 것이다. 부동 소수점형은 0.56과 같이 소수부가 있는 수를 나타낼 수 있다.
또한 매우 큰 값들을 나타낼 수 있다.

그럼 이어서 바로 학습해보자!


 

 

3. 부동 소수점수

 

부동 소수점수의 표기

부동 소수점수를 표기하는 방법은 두 가지가 있다.

  1. 일상생활에서 늘 사용하는 소수점 표기법
  2. 3.45E6과 같이 지수 표기를 사용

 

E6은 10의 6제곱을 의미하므로 3.45E6은 3,450,000이 된다. 여기서 6을 지 수, 3.45를 가수라 한다.
부동 소수점수의 지수 표기는 매우 큰 수나 매우 작은 수를 나타내는데 유용하다.

 

 

부동 소수점형

 

C++에는 float, double, long double의 세 가지 부동 소수점형이 있다. 이 데이터형들은 유효 숫자의 개수와 지수의 최소 허용 범위가 다르다.

 

예제 8번 : floatnum.cpp

// 부동 소수점형

#include <iostream>
int main()
{
 	using namespace std;
 	cout.setf(ios_base::fixed, ios_base::floatfield);
						// 고정 소수점 형식으로 출력
	float tub = 10.0 / 3.0;			// 유효 숫자 6개
	double mint = 10.0 / 3.0;		// 유효 숫자 15개
	const float million = 1.0e6;

	cout << "tub = " << tub;
	cout << ", a million tubs = " << million * tub;
	cout << ",\nten million tubs = ";
	cout << 10 * million * tub << endl;

	cout << "mint = " << mint << "and a million mints = ";
	cout << million * mint << endl;
	return 0;
}

 

지난번과 똑같이 풀었는데 이상하게도 이번에는 오류가 떠서 빌드가 잘 안되었다. 쓰기용 오류가 발생하는데 할 때마다 성가셔서 이 문제를 해결해야 할 것 같다.

 

 

프로그램 분석

일반적으로 cout은 뒤에 붙은 0을 출력하지 않는다. 그러나 cout.setf()를 호출하면 이 행동이 무시된다. 여기서 cout은 소수점 아래 6자리까지 출력하므로 0도 함께 출력된다.


 

 

부동 소수점수의 장단점

 

부동 소수점수는 정수에 비해 2가지 장점을 가진다.

  1. 정수와 정수 사이에 있는 값을 나타낼 수 있다
  2. 스케일을 사용하여 매우 큰 범위의 값을 나타낼 수 있다

 

예제 9번 : fltadd.cpp

// float형에서의 정밀도 손실 문제

#include <iostream>
int main()
{
	using namespace std;
	float a = 2.34E+22f;
	float b = a + 1.0f;

	cout << "a = " << a << endl;
	cout << "b - a = " << b - a << endl;
	return 0;
}

 

어떤 수에 1을 더한 후 원래의 수를 뺀다. 예상되는 결과는 1이어야 한다. 하지만 0이 되었다. 이는 2.34E+22f가 소수점 위로 23개의 숫자를 가진 큰 수이기 때문이다. 이 수에 1을 더하는 것은 23번째 숫자에 1을 더하는 것과 같다.
float형은 처음 6개나 7개의 숫자까지만 나타낼 수 있으므로, 23번째 숫자에 1을 더하는 것은 아무런 효과도 갖지 못한다.


 

4. C++ 산술 연산자

연산자와 피연산자가 결합되어 수식을 구성한다. 상수뿐 아니라 변수도 피연산자로 사용할 수 있다.

 

예제 10번 : arith.cpp

// C++의 산술 연산

#include <iostream>
int main()
{
    using namespace std;
    float hats, heads;			// 고정 소수점 형식으로 출력
    cout.setf(ios_base::fixed, ios_base::floatfield);
    cout << "수를 하나 입력하십시오: ";
    cin >> hats;
    cout << "다른 수를 입력하십시오: ";
    cin >> heads;

    cout << "hats = " << hats << "; heads = " << heads << endl;
    cout << "hats + heads = " << hats + heads << endl;
    cout << "hats - heads = " << hats - heads << endl;
    cout << "hats * heads = " << hats * heads << endl;
    cout << "hats / heads = " << hats / heads << endl;
    return 0;
}

 

 

 

연산 순서 : 우선순위와 결합 방향

C++에는 하나의 피연산자에 하나 이상의 연산자가 걸렸을 때 어느 연산잔를 먼저 적용할 것인지 결정하는 우선순위 규칙이라는 것이 있다.
연산자들의 결합 방향이 왼쪽에서 오른쪽인지 또는 오른쪽에서 왼쪽인지를 나타내는 결합 방향규칙을 적용한다. 왼쪽 -> 오른쪽이면 왼쪽에 있는 연산자를, 그 반대의 경우는 오른쪽 연산자를 먼저 적용한다.

 

예제 11번 : devide.cpp

// 정수 나눗셈과 부동 소수점수 나눗셈
  
#include <iostream>
int main()
{
	using namespace std;
	cout.setf(ios_base::fixed, ios_base::floatfield);
	cout << "정수 나눗셈: 9/5 = " << 9 / 5 << endl;
	cout << "부동 소수점수 나눗셈: 9.0/5.0 = ";
	cout << 9.0 / 5.0 << endl;
	cout << "혼합 나눗셈: 9.0/5 = " << 9.0 / 5 << endl;
	cout << "double형 상수: 1e7/9.0 = ";
	cout << 1.e7 / 9.0 << endl;
	cout << "float형 상수: 1e7f/9.0f = ";
	cout << 1.e7f / 9.0f << endl;
	return 0;
}

 

 

 

 

예제 12번 : modulus.cpp

// % 연산자를 사용하여 파운드를 스톤으로 변환한다

#include <iostream>
int main()
{
	using namespace std;
	const int Lbs_per_stn = 14;
	int lbs;

	cout << "당신의 체중을 파운드 단위로 입력하십시오: ";
	cin >> lbs;
	int stone = lbs / Lbs_per_stn;		// 몫은 스톤으로
	int pounds = lbs % Lbs_per_stn;		// 나머지는 파운드로
	cout << lbs << " 파운드는 " << stone
	      << " 스톤, " << pounds << " 파운드입니다.\n";
	return 0;
}


 

 

데이터형 변환

 

데이터 불일치를 해결하기 위해 다음가 같은 상황에서 자동으로 변환을 수행한다.

  • 특정 데이터형의 변수에 다른 데이터형의 값을 대입했을 때
  • 수식에 데이터형을 혼합하여 사용했을 때
  • 함수에 매개변수를 전달할 때

 

범위가 작은 데이터형의 값을 범위가 큰 데이터형에 대입하는 것은 젼혀 문제가 되지 않는다. 그러나 큰 long형의 값을 float형 변수에 대입하면 정밀도가 손실된다.

부동 소수점수를 정수형에 대입할 때는 두 가지 문제가 발생한다.

  1. 소수부를 버린다
  2. float형 값이 너무 클 경우 int형 변수에 온전히 들어갈 수 없다

 

예제 13번 : init.cpp

// 대입 구문에서 일어나는 데이터형 변환

#include <iostream>
int main()
{
    using namespace std;
    cout.setf(ios_base::fixed, ios_base::floatfield);
    float tree = 3;     	// int형을 float형으로 변환
    int guess(3.9832);  	// float형을 int형으로 변환
    int debt = 7.2E12;  	// 결과를 예측할 수 없다
    cout << "tree = " << tree << endl;
    cout << "guess = " << guess << endl;
    cout << "debt = " << debt << endl;
    return 0;
}

 

C++에서는 부동 소수점형을 정수형으로 변환할 때 소수부를 반올림하지 않고 매정하게 잘라버린다.

 

수식에서의 데이터형 변환

수식에 여러 가지 데이터형을 혼합해서 사용하면 어떤 일이 벌어지는가?
이 경우 두 종류의 자동 데이터형 변환을 수행한다.

  1. 어떤 데이터형은 나타날 때마다 자동으로 데이터형이 변환된다
  2. 다른 데이터형과 혼합하여 사용되었을 때 데이터형이 변환된다

 

정상 승급이 되는 경우가 몇 가지 있는데 이것까지 정리하면 분량이 너무 많은 것 같아 생략한다.

 

데이터형 변환자

데이터형 변환자를 사용하여 강제로 데이터형을 변환시킬 수 있다.

예제 14번 : typecast.cpp

// 강제 데이터형 변환

#include <iostream>
int main()
{
	using namespace std;
	int auks, bats, coots;

	// 다음 구문은 두 값을 double형으로 더한 후에
	// 그 합을 int형으로 변환하여 대입한다
	auks = 19.99 + 11.99;

	// 다음 두 구문은 두 값을 int형으로 변환한 후에 더한다
	bats = (int)19.99 + (int)11.99;		// 구식 C 스타일
	coots = int(19.99) + int(11.99);		// 신식 C++ 스타일
	cout << "바다오리 = " << auks << ", 박쥐 = " << bats;
	cout << ", 검둥오리 = " << coots << endl;

	char ch = 'Z';
	cout << "코드 " << ch << " 의 값은 ";	// char형으로 출력
	cout << int(ch) << endl;		// int형으로 출력
	cout << "네, 코드 Z의 값은 ";
	cout << static_cast<int>(ch) << endl;	// int형으로 출력
	return 0;
}

 

데이터형 변환자를 사용하는 2가지 이유

  1. double형으로 저장하지만 int형으로 계산해야 하는 값들에 사용된다
  2. 특정 목적에 맞게 강제로 데이터형을 변환한다

 

Chapter 3 3~4장 학습을 마치고

부동 소수점형은 정수형에 비해 어렵다고 생각했는데, 이것도 전보다는 더 친근하게 느껴졌다. 형 변환이 필요한 이유에 대해서도 잘 알게 되었고 분량이 많지만 데이터 처리 부분은 정말 중요하다.
이제 연습 문제를 풀며 단원을 마무리하고자 한다.