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

※ 소스코드

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


※ 결과



 

※ 요약
피타고라스의 정리를 이용한 두 점 사이의 거리 구하기다.
아래 그림과 같은 직각 삼각형이 있을때, 세 변의 길이는 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언어로 제작한 로또 프로그램을 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;
}

 

 


BMP 헤더 정보입니다.


//비트맵 파일에 대한 정보(파일 헤드)
typedef struct tagBITMAPFILEHEADER
{
        WORD       bfType ;            //"BM"이라는 값을 저장함
        DWORD      bfSize ;            //바이트 단위로 전체파일 크기
        WORD       bfReserved1 ;       //예약된 변수
        WORD       bfReserved2 ;       //예약된 변수
        DWORD      bfOffBits ;         //영상 데이터 위치까지의 거리
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER , *PBITMAPFILEHEADER;

typedef struct tagBITMAPINFOHEADER
{
        DWORD      biSize;             //이 구조체의 크기
        LONG       biWidth;            //픽셀 단위로 영상의 폭
        LONG       biHeight;           //영상의 높이
        WORD       biPlanes;           //비트 플레인 수(항상 1)
        WORD       biBitCount;         //픽셀당 비트수(컬러, 흑백 구별)
        DWORD      biCompression;      //압축 유무
        DWORD      biSizeImage;        //영상의 크기
        LONG       biXPelsPerMeter;    //가로 해상도
        LONG       biYPelsPerMeter;    //세로 해상도
        DWORD      biClrUsed;          //실제 사용 색상수
        DWORD      biClrImportant;     //중요한 색상 인덱스
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER , *PBITMAPINFOHEADER;



※설명

아래는 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;
}



※결과





※요약

어떤 수가 있을 때, 이 수가 2의 n승인지 판별하는 방법입니다.



※특징

비트 연산을 이용해 속도가 빠름



※함수 설명

2의 3승인 8을 2진수로 표현하면 아래와 같습니다.(편의상 8비트로 표현)

0000 1000

여기서 1을 빼주면

0000 0111이 되고, 0000 1000와 & 연산을 하게 되면 0이 나옵니다.


0000 1000

0000 0111

-------------&연산 (& 연산자는 두 개의 비트가 모두 1일 때 1이 됨, 고로 여기서는 0이 됨)

0000 0000


비트 연산 보기





※예제

#include <stdio.h>

bool CheckPowerOfTwo( int nNum );

int main( )
{
	int x = 0;
	bool bState = false;

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

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

		bState = CheckPowerOfTwo( x );

		printf( "%d은(는) 2의 n승이 %s\n\n", x, (bState ? "맞습니다." : "아닙니다.") );
	}

	return 0;
}

bool CheckPowerOfTwo( int nNum )
{
	int nResult = nNum & (nNum-1);

	if( nResult == 0 )
	{
		return true;
	}
	else
	{
		return false;
	}
}





※요약

rename : 파일 및 디렉토리의 이름을 변경한다.



※특징

이름 변경 말고도 파일을 이동할 때 사용할 수 있다.



※함수 원형 및 설명

int rename( const char *oldname, const char *newname );
//oldname : 파일 또는 디렉토리의 경로나 이름
//newname : 변경할 파일 또는 디렉토리의 이름
//반환값 : 성공 시 0, 실패 시 errno ( EACCES, ENOENT, EINVAL )



※예제

#include <stdio.h>
#include <direct.h>		//mkdir, rename

int main( )
{
	char strOldFolder[] = "d:\\TestOldFolder";
	char strNewFolder[] = "d:\\TestNewFolder";

	//테스트 폴더를 만든다.
	mkdir( strOldFolder );
	
	//이름을 바꾸고 결과를 리턴 받는다.
	int nResult = rename( strOldFolder, strNewFolder );

	if( nResult == 0 )
	{
		printf( "이름 변경 성공" );
	}
	else if( nResult == -1 )
	{
		perror( "이름 변경 실패 - " );
	}

	return 0;
}



※요약

_getdrive : 현재 작업 중인 드라이브를 구한다.



※함수 원형 및 설명

int _getdrive( );
//반환값 : A드라이브=1 ~ Z드라이브=26 일때 현재 드라이브 값



※예제 설명

아래 예제의 getdrive는 int형을 반환하지만 반환값이 1에서 26으로 정해져 있어 

char형 데이터 범위에 들어가므로 char형으로 변환해도 문제가 없다.

int형 데이터를 char형으로 명시적 형 변환을 해줘서 각 드라이브명을 출력하게 했다.

96을 더해준건 아스키 코드표를 보면 알 수 있다.



※예제

#include <stdio.h>
#include <direct.h>    //_getdrive

int main( )
{
	int nDrive = 0;

	nDrive = _getdrive( );

	printf( "현재 드라이브는 %c입니다.", char(nDrive)+96 );

	return 0;
}


※결과


+ Recent posts