※ 요약
C언어의 printf를 이용하여 float형이나 double형을 출력할때 소수점 이하 자릿수를 조절해서 출력하는 방법에 대한 글이다. C언어의 printf를 이용하여 자릿수를 정할 때는 보통 printf( "%.2f\n", 3.141592 );와 같이 자릿수를 설정하면 런타임 때 자릿수를 바꿀 수 없지만 이번에 포스팅하는 방법으로는 런타임 때도 자릿수를 바꿀 수 있어 좀 더 유연하게 출력이 가능하다.
C++의 cout도 궁금하다면 부동 소수점의 출력 정밀도 설정을 참고하기 바란다.

※ 소스코드

소스코드는 딱히 설명이 필요없을 정도로 간단하다. 필자는 예제를 위해 아래처럼 Print함수를 제작하였고, 필요에 따라 자릿수를 변경하여 출력하도록 하였다. 참고로 인자로 받을때 레퍼런스로 받도록 &를 붙였는데, 필자가 습관이 되서 붙인거니 C언어에서는 지워야지 에러없이 컴파일이 되니 참고바란다.


※ 결과



 

※ 요약
sin과 cos을 이용하여 직접 원을 그리는 방법이다.
직접 원을 그리므로 원하는 각도 만큼만 그릴수 있는데, 필자는 이를 신입때 만든 각도기 프로그램에 접목시켰다.
지금 이 예제는 Win32 API + OpenGL인데, MFC에서 GDI를 이용한 예제도 올리도록 하겠다.
또 타원 그리기 예제도 올릴것이고, 그 후에는 임의의 점이 원이나 타원에 속해 있는지 판별하는 예제도 올리도록 하겠다.
또 그후에는 타원이나 사각형 등의 도형을 직접 회전하는 예제도 올리겠다.

※ 특징
math.h에 있는 sin과 cos을 이용하여 원을 그린다.


DrawCircle2D가 원을 그리는 함수다. 배포하고 있는 프로그램이라 소스 코드의 일부만 올렸는데, 

MFC 예제를 올릴때는 전체 프로젝트를 올리겠다.

함수의 각 인자를 순서대로 설명하자면, 

원의 중심점, 시작 각도, 끝 각도, 원의 반경(반지름)이다.

각 인자의 용도를 알았으니 for문과 sin, cos을 이용하면 당장 MFC에서도 원을 그릴수 있을 것이다.

참고로 POINT는 float x, y가 멤버로 있는 구조체다.

또 아래 함수는 클래스의 멤버 함수이지만 C언어 함수로도 사용할 수 있어서 제목을 C/C++로 하였다.


아래는 위의 함수로 직접 그린 원이다.


아래는 다음 예제로 올릴 타원 그리기와 도형 회전이다.


※ 요약
이전에는 C언어로 두 점 사이의 거리를 구했는데, 같은 예제를 이번엔 C++로 작성해보았다.
사실 매일 하나의 글을 포스팅하기 위해 C언어와 C++을 나눠서 하느라 가끔 억지스러운 면이 없지 않아 있는거 같다.

※ 특징
C++을 이용해 피타고라스 정리를 구현하여 두 점 사이의 거리를 구한다.
좀 더 자세한 설명은 이전 글에 있으니 필요하다면 참고하기 바란다.

큰 틀은 C언어로 작성한 것과 같다. template 클래스와 template 함수로 작성하여 타입에 상관없이 거리를 구할 수 있게 하였다.
이렇게 template으로 만든 이유는, 윈도우 클라이언트 영역은 정수 타입이고, OpenGL이나 DirectX는 보통 실수 타입이기 때문이다.
또 필자는 아직 template 문법에 약하기 때문에 조금씩이나마 연습을 하고자 아래와 같이 작성하였다.


아래 화면은 상기 코드에 대한 디버깅 화면이다.

p1은 실수 타입으로, p2는 정수 타입으로 찍힌걸 보니 마음에 든다.

위 소스코드는 2010 기준으로 만들었다, 2013 이상이라면 GetDistance에서 double형으로 형변환을 안해줘도 에러가 발생하지 않는다.




#include <cmath>

template<typename T>
class CPoint
{
public:
	T x;
	T y;

public:
	CPoint( ) : x(0), y(0) { }
	CPoint( T _x, T _y ) : x(_x), y(_y) { }
};

template<typename T1, typename T2>
double GetDistance( const T1& p1, const T2& p2 )
{
	return std::sqrt( double( (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y) ) );
}

int main( )
{
	CPoint<double> p1( 432, 189 );
	CPoint<int> p2( 38, 392 );

	double dDistance = GetDistance( p1, p2 );

	return 0;
}


 

※ 요약
피타고라스의 정리를 이용한 두 점 사이의 거리 구하기다.
아래 그림과 같은 직각 삼각형이 있을때, 세 변의 길이는 a^2+b^2=c^2인 관계가 성립한다는 것이 피타고라스 정리다.


위 같은 공식을 아래 그림에 적용해보자.

먼저 빨간점 p1과 p2가 있고, 각각 가상의 수직선과 수평선을 그었다. 

그리고 가상의 노란 빗변을 이어주니 직각 삼각형이 되었다.

이제 a^2+b^2=c^2를 이용하여 두 점 사이의 거리를 구한다는게 느낌이 올 것이다.

(좀더 자세히 쓰려다가 그림 그리기도 귀찮고 수식 입력도 안되서 여기까지 작성함)


※ 특징
C언어를 이용해 피타고라스 정리를 구현하여 두 점 사이의 거리를 구한다.

C언어 소스는 아래와 같다. 임의의 점 p1(432, 189)과 p2(38, 392)의 거리를 측정해보니 443.2212가 나왔다.
헤더는 math.h를 추가해주고 GetDistance 함수와 구조체를 복사하면 어느 프로젝트에서든 두 점의 거리를 구할 수 있다.
C++를 이용하여 아래와 같은 예제를 구현하였으니 관심있으면 한 번 보기 바란다.



#include <stdio.h>
#include <math.h>

typedef struct tagPoint
{
	double x;
	double y;
}_Point;

double GetDistance( const _Point p1, const _Point p2 );

int main( )
{
	_Point p1 = { 432, 189 };
	_Point p2 = { 38, 392 };

	double dDistance = GetDistance( p1, p2 );

	return 0;
}

double GetDistance( const _Point p1, const _Point p2 )
{
	return sqrt( (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y) );
}


 

※ 로또 프로그램 요약
얼마 전 C++11 문법을 이용한 로또 프로그램을 올렸는데, 그 때 올린건 이전에 만든 C 스타일과의 비교를 위해 class를 이용하지 않았었다. 이번에 만든 건 class를 이용하여 만든 버전이다. 더불어 Sunny Holic님이 언급하신 std::shuffle 방식과 random 방식의 속도 비교도 해보았다.

※ 로또 프로그램 특징
- 이전에 만들었던 것과 다르게 class를 이용하여 제작하였다.
- random 방식과 std::shuffle 방식을 이용하여 번호를 생성하며, 두 방법의 속도를 비교 하였다.

아래는 예제 소스코드다, 소스코드는 첨부해뒀으니 Visual Studio 2013 이상이라면 바로 컴파일해 볼 수 있다.
- header file


main.cpp


아래는 속도를 비교한 것이다, 속도는 random 방식이 빠르지만 구현은 shuffle 방식이 더 간단하다는 장점이 있다.

또 random 방식은 다른 곳에 응용(더 많은 수를 중복 없이 생성하는 곳)해도 빠른 속도를 보장 받을 수 있다는 장점이 있다.



※ 로또 프로그램 요약
C++11 문법을 이용하여 제작한 간단한 로또 프로그램이다.

전에 C언어로 제작한 로또 프로그램을 C++11 문법으로 리팩토링 하였다.

알고리즘 및 큰 흐름은 거의 같고 문법만 C에서 C++11로 바꿨으니 둘을 비교하면서 보면 좋을 것이다.


※ 로또 프로그램 특징
- C++11에 추가된 새로운 random(Mersenne Twister)을 이용하여 속도가 빠르고 품질이 좋다.

- 중복 판별을 위한 인덱스를 따로 관리하여 요소 개수가 많더라도 중복 판별에 상수 시간 복잡도를 갖을 수 있어 매우 빠르다.

- 표준 문법으로만 작성하여 윈도우, 리눅스 모두 컴파일 가능하다. (단 VS2013(12.0) 이상, gcc 4.8 이상)

 

#include <iostream>	//std::cout
#include <random>		//std::mt19937_64
#include <array>		//std::array
#include <algorithm>	//std::sort
#include <functional>	//std::greater

#define _NUMBER_MAX 6
#define _LOTTTO_MAX 45

int GetNumber( const int nMin, const int nMax );
std::ostream& operator<<( std::ostream& ostr, const std::array<int, _NUMBER_MAX>& arr );

int main( )
{
	//변수 선언
	std::array<int, _NUMBER_MAX> nNumber = { 0, };	//생성된 번호가 담길 배열
	std::array<bool, _LOTTTO_MAX> bIndex = { false, };	//빠른 중복 판단을 위한 배열 

	//번호 생성
	for( unsigned int i=0 ; i<nNumber.size() ; )
	{
		const auto nTempNum = GetNumber( 1, _LOTTTO_MAX );	//랜덤으로 번호 생성

		if( bIndex[nTempNum-1] == false )			//생성된 번호와 인덱스를 비교
		{
			bIndex[nTempNum-1] = true;			//생성된 번호와 인덱스 값 변경
			nNumber[i++] = nTempNum;			//생성된 번호 삽입
		}
	}

	//정렬, 주석을 지우면 내림차순 정렬한다.
	std::sort( nNumber.begin(), nNumber.end()/*, std::greater<int>( )*/ );
	
	//출력
	std::cout<<nNumber;

	return 0;
}

int GetNumber( const int nMin, const int nMax )
{
	std::random_device rn;		//시드 값
	std::mt19937_64 rnd( rn() );	//random_device를 시드 값으로 하여 초기화

	//랜덤의 범위를 지정
	std::uniform_int_distribution<int> nDist( nMin, nMax );
	
	return nDist( rnd );
}

std::ostream& operator<<( std::ostream& ostr, const std::array<int, _NUMBER_MAX>& arr )
{
    for( auto &i : arr )
    {
        ostr<<" "<<i;
    }

    return ostr;
}

 

 


친구가 임의의 위치와 임의의 크기의 원 안에만 점을 찍고 싶다고 해서 만든 함수 입니다.

함수의 첫 번째 인자는 원의 중심 위치고 두 번째는 반지름, 세 번째는 점의 크기입니다.

랜덤 함수로는 C++11의 random헤더에 있는 random_device와 mt19937_64를 사용 했습니다.

원 안에만 점을 찍는 이유는 포탄이나 탄알이 조준 한 곳에 안 맞고 오차가 있기 때문입니다.

서든어택이나 AVA 등의 FPS 게임 해보신 분들은 잘 아실듯요.

원 그리기는 이 링크에 있습니다.



※함수

void CFigure::DrawRandomPoint2D( const POINT ptPos, const float nRadius, const float ptSize /*= 2.0f*/ ) const
{
	POINT ptPoint;

	//랜덤 함수
	std::random_device rn;			//시드 값
	std::mt19937_64 rnd( rn() );	//random_device를 시드 값으로 하여 초기화

	//랜덤의 범위를 0~360 범위 내에서 지정
	std::uniform_real_distribution<float> nDist( 0.0f, 360.0f );
	float fTempDegree = nDist( rnd );
	
	//반지름의 범위를 0에서 입력받은 값 범위 내에서 지정
	std::uniform_real_distribution<float> fDist( 0.0f, nRadius );
	float fTempRadius = fDist( rnd );

	//랜덤으로 뽑은 각과 반지름으로 점 찍을 위치 계산
	ptPoint.x = _SCAST( long, cos( fTempDegree * DEG2RAD ) * fTempRadius );
	ptPoint.y = _SCAST( long, sin( fTempDegree * DEG2RAD ) * fTempRadius );

	//입력 받은 점 기준으로
	ptPoint.x += ptPos.x;
	ptPoint.y += ptPos.y;

	this->DrawPoint2D( ptPoint, ptSize );
}






동영상 입니다.

하얀색 원은 편의상 그린 원 입니다



이건 원과 원 사이에만 점 찍기 입니다.


※설명

아래는 C언어로 만든 로또 번호 생성 예제이자 중복 없는 난수 생성 예제다. 

본인이 배포하는 로또 프로그램에서 일부 가져와 군더더기를 제거하고 예제에 맞게 수정했다.

중복을 방지하기 위해 생성 여부에 대한 일종의 인덱스를 둬서 따로 검사하지 않아도 중복을 방지할 수 있게 했다.

흔히 사용하는 배열 요소를 처음부터 끝까지 조사 방식은 배열 개수에 따라 속도가 엄청 느려지므로 추천하지 않으며

이렇게 인덱스를 두는 방법은 에라토스테네스의 체를 구할 때도 이용되고

데이터를 캐싱할 때 이미 불러들인 데이터인지 판별할 때도 사용된다.

C++로 구현한 로또 프로그램도 있으니 비교해보니 바란다.



※예제

#include <stdio.h>
#include <stdlib.h>		//srand
#include <time.h>	    //time

//#define _MAX 6
const int nMAX = 6;

void BubbleSort( int *nArr, int nArrSize );

int main( )
{
	//변수 생성 및 초기화 부분
	srand( (unsigned)time(NULL) );				//srand로 초기화

	int nLottoNum[nMAX] = { 0, };				//생성된 로또 번호를 저장할 변수
	bool bCheckExistOfNum[45] = { false, };		//생성된 번호가 중복인지 체크할 변수(인덱스)

	//번호 생성 부분
	for( int i=0 ; i<nMAX ; )				    //번호가 6개 생성될 때까지 돈다.
	{
		int nTemp = rand()%45;					//0~44 범위의 번호를 생성한다.

		if( bCheckExistOfNum[nTemp] == false )	//중복 여부 판단
		{
			bCheckExistOfNum[nTemp] = true;		//번호가 중복 생성되지 않게 존재 여부를 true로 한다.
			nLottoNum[i] = nTemp+1;				//+1을 안해주면 범위가 0~44이다.
			++i;								//증감 연산을 for문에서 하지 않고 여기서 한다.
		}
	}

	//버블 정렬
	BubbleSort( nLottoNum, sizeof(nLottoNum)/sizeof(int) );

	//출력 부분
	for( int i=0 ; i<nMAX ; ++i )
	{
		printf( "%2d ", nLottoNum[i] );
	}

	return 0;
}

void BubbleSort( int *nArr, int nArrSize )
{
	for( int i=0 ; i<nArrSize-1 ; i++ )
	{
		for( int j=0 ; j<nArrSize-(i+1) ; j++ )
		{
			if( nArr[j] > nArr[j+1] )	//꺽쇠 방향으로 오름 차순, 내름 차순 결정
			{//값 교환 부분
				int temp = nArr[j];
				nArr[j] = nArr[j+1];
				nArr[j+1] = temp;
			}
		}
	}
}



※요약

재귀함수를 통해 10진수2진수로 변환한다.



※특징

10진수 n을 Dec2Binary함수에 넣으면 2진수로 출력해준다.

int형으로 값을 받으므로 2,147,483,647의 수까지 변환이 되며,

더 큰 수를 변환하고 싶으면 unsigned int나 unsigned __int64로 하면 된다.

unsigned __int64의 경우 18446744073709551615의 수를 2진수로 변환 할 수 있다.



※예제

#include <stdio.h>

void Dec2Binary( unsigned __int64 nNum );

int main( )
{
	unsigned __int64 nNum = 0;

	printf( "-1 입력시 종료 됩니다.\n" );

	while( nNum != -1 )
	{
		printf( "숫자를 입력하세요 : " );
		scanf( "%d", &nNum );

		Dec2Binary( nNum );
		printf( "\n\n" );
	}

	return 0;
}

void Dec2Binary( unsigned __int64 nDecimal )
{
	if( nDecimal < 2 )
	{
		printf( "%I64u", nDecimal );
	}
	else
	{
		Dec2Binary( nDecimal/2 );
		printf( "%I64u", nDecimal%2 );
	}
}


※결과


-unsigned __int64형으로 했을 때 변환할 수 있는 최대의 수



※요약

C언어나 C++ 등에서 소수점 특정 자릿수 반올림하는 방법입니다. 매크로 함수와 일반 함수 2가지로 구현되어 있으며, 다른 곳에서 사용할 땐 원하는 함수만 복사해서 쓰면 됩니다.


※예제

#include <stdio.h>
#include <cmath>

#define ROUNDING(x, dig)	( floor((x) * pow(float(10), dig) + 0.5f) / pow(float(10), dig) )

double Rounding( double x, int digit )
{
	return ( floor( (x) * pow( float(10), digit ) + 0.5f ) / pow( float(10), digit ) );
}

int main( )
{
	printf( "%g\n", ROUNDING( 9.3453456, 3 ) );
	printf( "%g\n", Rounding( 9.3453456, 3 ) );
	printf( "%g\n", ROUNDING( 9.3453456, 5 ) );
	printf( "%g\n", Rounding( 9.3453456, 5 ) );

	return 0;
}



※결과


+ Recent posts