전처리기 지시문

#define , #error, #if / #elif / #else 및 #endif / #ifdef와 #ifndef

 

 

 

전처리기 지시문은 일반적으로 소스 프로그램을 쉽게 변경하고 여러 실행 환경에서 쉽게 컴파일할 수 있도록 하기 위해 사용된다.

전처리기(=선행처리)란 컴파일 이전의 처리를 의미하고 소스파일은 컴파일러에 의해 컴파일 되기 이전에 이 전처리기 단계를 거친다.

 

 

 

◎ #define 지시문

이 #define 지시문은 세 부분으로 나뉜다.

#define PI 3.1415
지시자 매크로 매크로 몸체

많이 쓰이는 PI로 예시를 들어보면 코드에서 PI라는 이름(매크로)이 나오면 3.1415(매크로 몸체)로 바꾸라는 명령과 같다.

 

#define PI 3.1415

int function_before()
{
	int angle;
	angle = 180 * PI;
}

int funcint_after()
{
	int angle;
	angle = 180 * 3.1415;
}

 

전처리기의 단계를 거치면 코드 상에서 function_before의 내용을 function_after로 바꾼다.

이렇게 상수를 매크로로 정의한 것을 매크로 상수라고 부르고

상수가 아닌 함수로 정의해 놓은 것을 매크로 함수라고 부른다. 

 

 

 

 

◎ #if, #elif, #else 및 #endif 지시문 

#if... #endif는 조건부 코드 삽입을 위한 지시문이다. 

소스파일의 각 #if 지시문은 #endif 지시문과 일치해야 하며 #if와 #endif 사이에는 여러개의 #elif가 나올 수 있지만 #else는 하나만 사용할 수 있다.

 

#define ENABLE 1
#define IMPOSSIBLE 0


int main()
{
#if ENABLE
	cout << "ENABLE" << "\n";
#endif IMPOSSIBLE
	cout << "IMPOSSIBLE" << "\n";
}

 사용법은 이처럼 if문이 나오면 반드시 endif가 나와야 하며 ENABLE이 참(=1)이라면 cout << "ENABLE" << "\n"; 의 코드를 삽입하고 IMPOSSIBLE이 참이라면 cout << "IMPOSSIBLE" << "\n";의 코드를 삽입하라는 뜻이다. 

 

 

 

 

 

 

◎ #ifdef 및 #ifndef 지시문

정의 된 연산자와 함께 사용될 때 #if 지시어와 동일한 효과를 가진다.

 

#ifndef 식별자
#ifndef 식별자

이 지시문은 아래의 지시문과 같은 내용이다.

#if 정의된 식별자
#if! 정의된 식별자

 

#if는 매크로가 참인지 거짓인지에 따라서 동작한다면 #ifdef는 매크로가 '정의되어 있는지 아닌지'를 기준으로 동작한다. 

#ifdef는 매크로가 정의되어 있다면이고

#ifndef는 매크로가 정의되어 있지 않다면이라는 의미이다. 

 

 

 

 

◎ #error 지시문

전처리기가 해당 지시문을 발견하면 컴파일이 중지되고 여기에 포함된 오류 메시지가 전달된다. 

#define _EXIT

#ifdef _EXIT
#error "Error exists."
#endif

 

전처리기가 해당 코드를 만나게 되면 error.c : error: #error "Error exists." 이렇게 출력되며 종료된다. 

 

 

 

 

 

이 외에도 #import, #include, #line, #undef, #using 등이 전처리기 지시문에 포함된다. 

 

 

 

 

 

참고 자료;

 

https://docs.microsoft.com/ko-kr/cpp/preprocessor/preprocessor-directives?view=vs-2019

 

전처리기 지시문

전처리기 지시문Preprocessor directives 이 문서의 내용 --> #define, #ifdef과 같은 전처리기 지시문은 일반적으로 소스 프로그램을 쉽게 변경하고 여러 실행 환경에서 쉽게 컴파일할 수 있도록 하는데 사용됩니다.Preprocessor directives, such as #define and #ifdef, are typically used to make source programs easy to change and easy to

docs.microsoft.com

 

https://programmers.co.kr/learn/courses/30/lessons/42588

 

코딩테스트 연습 - 탑 | 프로그래머스

수평 직선에 탑 N대를 세웠습니다. 모든 탑의 꼭대기에는 신호를 송/수신하는 장치를 설치했습니다. 발사한 신호는 신호를 보낸 탑보다 높은 탑에서만 수신합니다. 또한, 한 번 수신된 신호는 다른 탑으로 송신되지 않습니다. 예를 들어 높이가 6, 9, 5, 7, 4인 다섯 탑이 왼쪽으로 동시에 레이저 신호를 발사합니다. 그러면, 탑은 다음과 같이 신호를 주고받습니다. 높이가 4인 다섯 번째 탑에서 발사한 신호는 높이가 7인 네 번째 탑이 수신하고, 높이가 7

programmers.co.kr

 

더보기

수평 직선에 탑 N대를 세웠습니다. 모든 탑의 꼭대기에는 신호를 송/수신하는 장치를 설치했습니다. 발사한 신호는 신호를 보낸 탑보다 높은 탑에서만 수신합니다. 또한, 한 번 수신된 신호는 다른 탑으로 송신되지 않습니다.

예를 들어 높이가 6, 9, 5, 7, 4인 다섯 탑이 왼쪽으로 동시에 레이저 신호를 발사합니다. 그러면, 탑은 다음과 같이 신호를 주고받습니다. 높이가 4인 다섯 번째 탑에서 발사한 신호는 높이가 7인 네 번째 탑이 수신하고, 높이가 7인 네 번째 탑의 신호는 높이가 9인 두 번째 탑이, 높이가 5인 세 번째 탑의 신호도 높이가 9인 두 번째 탑이 수신합니다. 높이가 9인 두 번째 탑과 높이가 6인 첫 번째 탑이 보낸 레이저 신호는 어떤 탑에서도 수신할 수 없습니다.

송신 탑(높이)수신 탑(높이)
5(4) 4(7)
4(7) 2(9)
3(5) 2(9)
2(9) -
1(6) -

맨 왼쪽부터 순서대로 탑의 높이를 담은 배열 heights가 매개변수로 주어질 때 각 탑이 쏜 신호를 어느 탑에서 받았는지 기록한 배열을 return 하도록 solution 함수를 작성해주세요.

제한 사항

  • heights는 길이 2 이상 100 이하인 정수 배열입니다.
  • 모든 탑의 높이는 1 이상 100 이하입니다.
  • 신호를 수신하는 탑이 없으면 0으로 표시합니다.

입출력 예


[6,9,5,7,4] [0,0,2,2,4]
[3,9,9,3,5,7,2] [0,0,0,3,3,3,6]
[1,5,3,6,7,6,5] [0,0,2,0,0,5,6]

입출력 예 설명

입출력 예 #1
앞서 설명한 예와 같습니다.

 

입출력 예 #2

[1,2,3] 번째 탑이 쏜 신호는 아무도 수신하지 않습니다.
[4,5,6] 번째 탑이 쏜 신호는 3번째 탑이 수신합니다.
[7] 번째 탑이 쏜 신호는 6번째 탑이 수신합니다.

 

입출력 예 #3

[1,2,4,5] 번째 탑이 쏜 신호는 아무도 수신하지 않습니다.
[3] 번째 탑이 쏜 신호는 2번째 탑이 수신합니다.
[6] 번째 탑이 쏜 신호는 5번째 탑이 수신합니다.
[7] 번째 탑이 쏜 신호는 6번째 탑이 수신합니다.

 

 

 

#include <string>
#include <vector>

using namespace std;

vector<int> solution(vector<int> heights) {
	vector<int> answer(heights.size());

	for (int i = heights.size() - 1; i >= 0; i--) {

		for (int j = i - 1; j >= 0; j--) {
			if (heights[i] < heights[j]) {
				answer[i] = j+1;
				break;
			}
		}
	}
	return answer;
}

 

 

 

 

클래스(Class)와 구조체(Struct)의 차이

구조체에 대한 설명과 클래스와 구조체의 차이

 

 

 

연관있는 데이터를 하나로 묶는다는 기본 개념은 동일하다. 그러면 각각은 무엇이고 둘의 차이는? 

 

◎ 구조체

C++에서 구조체를 선언하는 방법

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
}

 

 

C++에서는 C에서 처럼 typedef를 추가할 필요 없이 사용이 가능하다.

int main()
{
	My_S info = {"hi", 90, 100}
	
	info.name = "hello";
	info.age = 100;
	info.number = 7;
}

 

 

구조체와 클래스를 비교하면서 구조체에는 함수를 추가하지 못한다는 이상한 내용도 가끔 보이는데 아니다.

C++에서는 구조체 안에도 함수 추가 가능하다

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
    
    void SetMyAge(int _age)
	{
		age = _age;
	}
}

 

여기서 내가 My_S 구조체의 변수를 여러 개 선언해서 메모리 공간에 구조체 변수가 할당이 될 때, 

함수는 모든 구조체 변수 내에 각각 별도로 존재하는 것이 아니다.

구조체 안의 함수는 한 번만 선언된 하나의 함수를 공유한다. 

 

 

그리고 물론 이 함수를 외부로 뺄 수도 있다.

Struct My_S
{
	char name[MAX_LEN];
	int age;
	int number;
    
    void SetMyAge(int _age);
}

void My_S::SetMyAge(int _age)
{
	age = _age;
}

 

함수 원형 선언은 구조체 안에 두고 정의를 밖으로 빼서 정의하는 것이다.

딱히 쓸 일이 없어 보이지만 구조체를 보는 순간 정의와 기능을 한 눈에 파악하기 쉽게 작성하는 데 도움이 되기도 한다. 

 

그런데 구조체 안에 함수가 정의 되어있을 때와 바깥에 있을 때 다른 점이 하나 존재한다.

바로 구조체 안에 있을 때는 함수가 inline으로 처리되고 있다는 점이다. 

인라인의 의미를 그대로 유지하고 싶다면 inline 키워드를 사용해서 명시적으로 지시하면 된다. 

 

 

 

 

 

◎ 클래스

구조체는 클래스의 일종이다.

struct를 사용하면 구조체, class를 사용하면 클래스가 되는 것이다.

Class My_S
{
	char name[MAX_LEN];
	int age;
	int number;
}

위의 구조체와 비교했을 때 기본 선언 시 사용하는 키워드만 다를 뿐이다.

 

 

그런데 이 키워드의 차이는 초기화의 방법의 차이를 발생시킨다.

int main()
{
	My_S info = {"hi", 90, 100};
}

struct는 이와같은 방법으로 초기화가 가능했지만 클래스에서는 불가능하다.

그 이유는 클래스 내에 선언된 함수가 아니고 다른 영역에서 초기화를 하려고 했기 때문이다. 

클래스는 기본적으로 별도의 선언이 없다면 클래스 내에 선언된 변수는 클래스 내에 선언된 함수에서만 접근이 가능하다. 

 

여기서 접근에 대한 구분이 존재한다. 그것을 접근제어 지시자라고 하는데

public 모두에게 공개; 아무나 접근 가능
protected 상속관계로 얽혀있는 클래스에서만 접근허용
private 클래스 안에서만 건들일 수 있다.

 

접근제어 지시자를 명시하지 않으면 class는 기본으로 private으로 설정한다. 

그래서 위의 class는 private으로 지정되어 있어 외부에서 접근이 불가능한 것이다. 

 

 

이 접근제어 지시자는 구조체에서도 선언이 가능하다.

단, 클래스에서는 따로 지정하지 않으면 private으로 설정이 되지만 구조체에서는 public으로 설정이 된다. 

 

 

 

 

 

 

 

 

 

참고 자료;

윤성우의 열혈 C++ 프로그래밍 ( 저; 윤성우, 출판사; 오렌지 미디어)

'Study > C++ , C#' 카테고리의 다른 글

[C#] 닷넷 API  (0) 2021.12.22
[C++] 전처리기 지시문  (0) 2020.01.19
[C++] QueryPerformanceCounter / 프로그램 실행 시간 측정  (0) 2020.01.15
[C++] 매크로 함수와 인라인 함수(Inline)  (0) 2020.01.10
[C++] new 와 delete  (0) 2020.01.10

 

 

QueryPerformance을 이용해 프로그램 실행 속도 측정

컴퓨터 메인보드의 고해상도 타이머를 이용해 시간 간격을 측정한다.

 

 

 

 

현재 실행 속도를 측정하고 싶으면 QueryPerformanceCounter(=QPC)를 이용하면 된다.

하지만 QPC는 외부 시간 참조와 독립적이며 동기화되지 않으므로 현재 시간값을 구하고 싶으면 GetSystemTimePreciseAsFildTime을 이용하라고 MSDN에 나와있다. 

 

QueryPerformanceFrequency와 QueryPerformanceCounter를 이용하면 타이머, FPS 측정 등 여러 방면으로 활용할 수 있다. 

 

 

 

 

◎ QueryPerformanceFrequency

성능 카운터의 빈도를 검색한다. 

 

BOOL QueryPerformanceFrequency(
  LARGE_INTEGER *lpFrequency
);

 

lpFrequency = 현재 성능 카운터, 타이머의 주파수를 반환한다.

 

 

 

 

 

◎ QueryPerformanceCounter

시간 간격 측정에 사용할 수 있는 고해상도 타임 스탬프인 성능 카운터의 현재 값을 검색한다. 그러니까 현재 CPU의 틱을 받아오는 것이다. 

 

BOOL QueryPerformanceCounter(
  LARGE_INTEGER *lpPerformanceCount
);

 

lpPerformanceCount = 매개변수로 현재 성능 카운터 값을 계수로 받는 변수에 대한 포인터를 넘겨준다. 

 

반환 값은 함수가 성공하면 0 이외의 값을 리턴하고 실패하면 0을 리턴한다. 

(Windows XP 이상에서는 이 기능이 항상 성공해서 0을 리턴하는 경우는 없다.)

 

 

 

 

 

◎ LARGE_INTEGER

여기서 쓰이는 LARGE_INTEGER는 뭘까?

QueryPerformanceCounter나 QueryPerformanceFrequency같은 함수를 사용하기 위해선 크기가 큰 정수형이 필요하다. 

왜? 더 자세한 시간값을 저장하기 위해서 

그래서 windows.h에 포함돼 있는 LARGE_INTEGER가 그와 같은 것이다. 

부호가 있는 64비트 정수형 데이터를 저장하기 위해 선언된 사용자 정의 자료형.

 

LARGE_INTEGER는 구조체인데 그 안을 들여다 보면

typedef union _LARGE_INTEGER {
  struct {
    DWORD LowPart;	// 32bit 정수형
    LONG  HighPart;	// 32비트 정수형
  } DUMMYSTRUCTNAME;
  struct {
    DWORD LowPart;
    LONG  HighPart;
  } u;
  LONGLONG QuadPart;	// 64비트 정수형
} LARGE_INTEGER;

이렇게 되어 있다. 

컴파일러가 64비트를 지원할 땐 64비트 정수형 변수에, 32비트 지원 시에는 32비트 정수형 변수에 64비트 측정값을 나눠 저장한다. 

 

값은 64비트의 부호있는 정수형인 QuardPart에 저장되는 것이며 

LowPart는 하위 32비트 DWORD형, HighPart는 상위 32비트 LONG 형이다.

 

64비트 중 LowPart(32bit)와 HighPart(32bit)를 둘다 사용함으로써 더 큰값을 사용할 수 있다. 

 

 

 

 

 

◎ 사용법

#include <windows.h>

int main()
{
	LARGE_INTEGER timer, start, end;
	float DeltaTime;
    
	QueryPerformanceFrequency(&timer); // 타이머의 주파수를 얻어온다. 
   
   
	QueryPerformanceCounter(&start);  // 시작 시점의 CPU 클럭 수
    
    
    // 실행할 내용
    
    
	QueryPerformanceCounter(&end);	// 종료 시점의 CU 클럭 수
    
	DeltaTime = (end.QuadPart - start.QuadPart) / (float)timer.QuadPart; 	// 걸린 시간 계산
 
 }

 

 

 

 

 

 

참고 자료;

 

https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency

 

QueryPerformanceFrequency function - Win32 apps

Retrieves the frequency of the performance counter.

docs.microsoft.com

 


https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter

 

QueryPerformanceCounter function - Win32 apps

Retrieves the current value of the performance counter, which is a high resolution (<1us) time stamp that can be used for time-interval measurements.

docs.microsoft.com

 

 

'Study > C++ , C#' 카테고리의 다른 글

[C++] 전처리기 지시문  (0) 2020.01.19
[C++] 클래스(Class)와 구조체(Struct)의 차이  (0) 2020.01.15
[C++] 매크로 함수와 인라인 함수(Inline)  (0) 2020.01.10
[C++] new 와 delete  (0) 2020.01.10
[C++] 참조자와 함수  (0) 2020.01.08

 

https://programmers.co.kr/learn/courses/30/lessons/12980

 

코딩테스트 연습 - 점프와 순간 이동 | 프로그래머스

OO 연구소는 한 번에 K 칸을 앞으로 점프하거나, (현재까지 온 거리) x 2 에 해당하는 위치로 순간이동을 할 수 있는 특수한 기능을 가진 아이언 슈트를 개발하여 판매하고 있습니다. 이 아이언 슈트는 건전지로 작동되는데, 순간이동을 하면 건전지 사용량이 줄지 않지만, 앞으로 K 칸을 점프하면 K 만큼의 건전지 사용량이 듭니다. 그러므로 아이언 슈트를 착용하고 이동할 때는 순간 이동을 하는 것이 더 효율적입니다. 아이언 슈트 구매자는 아이언 슈트를 착용

programmers.co.kr

 

 

 

 

 

더보기

OO 연구소는 한 번에 K 칸을 앞으로 점프하거나, (현재까지 온 거리) x 2 에 해당하는 위치로 순간이동을 할 수 있는 특수한 기능을 가진 아이언 슈트를 개발하여 판매하고 있습니다. 이 아이언 슈트는 건전지로 작동되는데, 순간이동을 하면 건전지 사용량이 줄지 않지만, 앞으로 K 칸을 점프하면 K 만큼의 건전지 사용량이 듭니다. 그러므로 아이언 슈트를 착용하고 이동할 때는 순간 이동을 하는 것이 더 효율적입니다. 아이언 슈트 구매자는 아이언 슈트를 착용하고 거리가 N 만큼 떨어져 있는 장소로 가려고 합니다. 단, 건전지 사용량을 줄이기 위해 점프로 이동하는 것은 최소로 하려고 합니다. 아이언 슈트 구매자가 이동하려는 거리 N이 주어졌을 때, 사용해야 하는 건전지 사용량의 최솟값을 return하는 solution 함수를 만들어 주세요.

예를 들어 거리가 5만큼 떨어져 있는 장소로 가려고 합니다.
아이언 슈트를 입고 거리가 5만큼 떨어져 있는 장소로 갈 수 있는 경우의 수는 여러 가지입니다.

  • 처음 위치 0 에서 5 칸을 앞으로 점프하면 바로 도착하지만, 건전지 사용량이 5 만큼 듭니다.
  • 처음 위치 0 에서 2 칸을 앞으로 점프한 다음 순간이동 하면 (현재까지 온 거리 : 2) x 2에 해당하는 위치로 이동할 수 있으므로 위치 4로 이동합니다. 이때 1 칸을 앞으로 점프하면 도착하므로 건전지 사용량이 3 만큼 듭니다.
  • 처음 위치 0 에서 1 칸을 앞으로 점프한 다음 순간이동 하면 (현재까지 온 거리 : 1) x 2에 해당하는 위치로 이동할 수 있으므로 위치 2로 이동됩니다. 이때 다시 순간이동 하면 (현재까지 온 거리 : 2) x 2 만큼 이동할 수 있으므로 위치 4로 이동합니다. 이때 1 칸을 앞으로 점프하면 도착하므로 건전지 사용량이 2 만큼 듭니다.

위의 3가지 경우 거리가 5만큼 떨어져 있는 장소로 가기 위해서 3번째 경우가 건전지 사용량이 가장 적으므로 답은 2가 됩니다.

 

 

 

입출력 예

 

N Result
5 2
6 2
5000 5

입출력 예 설명

입출력 예 #1
위의 예시와 같습니다.

 

입출력 예 #2
처음 위치 0 에서 1 칸을 앞으로 점프한 다음 순간이동 하면 (현재까지 온 거리 : 1) x 2에 해당하는 위치로 이동할 수 있으므로 위치 2로 이동합니다. 이때 1 칸을 앞으로 점프하면 위치3으로 이동합니다. 이때 다시 순간이동 하면 (현재까지 온 거리 : 3) x 2 이동할 수 있으므로 위치 6에 도착합니다. 이 경우가 건전지 사용량이 가장 적으므로 2를 반환해주면 됩니다.

 

입출력 예 #3
위와 같은 방식으로 합니다.

 

 

#include <iostream>
using namespace std;

int solution(int n)
{
	int ans = 0;
	int remain_meter = n;
	for (int i = remain_meter; i > 0; i /= 2) {
		if (i % 2 == 1) {
			ans += 1;
		}
	}
	return ans;
}

 

 

 

 

 

https://www.acmicpc.net/problem/11279

 

11279번: 최대 힙

첫째 줄에 연산의 개수 N(1≤N≤100,000)이 주어진다. 다음 N개의 줄에는 연산에 대한 정보를 나타내는 정수 x가 주어진다. 만약 x가 자연수라면 배열에 x라는 값을 넣는(추가하는) 연산이고, x가 0이라면 배열에서 가장 큰 값을 출력하고 그 값을 배열에서 제거하는 경우이다. 입력되는 자연수는 2^31보다 작다.

www.acmicpc.net

 

 

 

 

 

 

계속 시간초과 오류가 발생하였다.

 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
	int N;
	cin >> N;
	vector<int> arr;
	arr.reserve(N);

	for (int i = 0; i < N; i++)
	{
		int temp = 0;
		cin >> temp;

		if (temp == 0)
		{
			if (!arr.empty())
			{
				sort(arr.begin(), arr.end(),greater<int>());
				cout << arr[0] << "\n";
				arr.erase(arr.begin());
			}
			else
			{
				cout << "0" << "\n";

			}
		}
		else {
			arr.push_back(temp);
		}
	}
}

 

 

이게 처음에 작성한 코드이고 시간초과가 계속 발생해서 우선순위 큐로 바꿔봐야 하나?

하고 우선순위 큐로 다시 작성한 코드가 아래 코드이다.

 

#include <iostream>
#include <queue>
using namespace std;

int main()
{
	int N;
	cin >> N;
	priority_queue<int> arr;


	for (int i = 0; i < N; i++)
	{
		int temp;
		cin >> temp;

		if (temp == 0)
		{
			if (!arr.empty())
			{
				cout << arr.top() << "\n";
				arr.pop();
			}
			else
			{
				cout << "0\n";
			}
		}
		else {
			arr.push(temp);
		}
	}
	return 0;
}

1번에서는 6%대에서 2번에서는 31%대에서 시간초과 오류가 계속 발생한다.

어디서 뭘 더 어떻게 줄여야 하는지 잘 모르겠다; 

 

혹시 몰라서 C++14로 체크하던걸 C++11으로 바꿨지만 여전히 똑같았다.

우선순위 큐로도 해결되지 않는다면 이건 대체 어떻게 풀어야 하는 걸까... 

정말 문제 제목대로 힙을 구현해야만 해결할 수 있는 문제인가?

근데 아무리 그래도 우선순위 큐로 구현한 것도 시간초과가 뜨나? 

 

 

 

XNA Math 라이브러리와 XMVECTOR

Direct3D 11의 D3DX 라이브러리에는 Direct3D 9와 10의 D3DX와는 다르게 개별적으로 개발된

XNA Math 라는 벡터 수학 라이브러리가 제공된다. 

 

 

 

 

 

 

SIMD의 명령들은 128비트 너비의 SIMD(Single instruction multiple data) 레지스터들을 이용해서 한 명령에서 32비트 float나 int 네 개를 한 번에 처리할 수 있다. 이는 벡터 계산에 아주 유용하다.

SIMD가 아닌 경우에는 32-비트의 실수를 4번 더해야 하지만 벡터의 덧셈 시 SIMD를 이용하면 두 개의 4요소 벡터(Vector4) 를 더할 경우 한 번의 덧셈 명령으로 처리가 가능하다는 것이다. 

 

SIMD?

더보기

병렬 프로세서의 한 종류로, 하나의 명령어로 여러 개의 값을 동시에 계산하는 방식, 컴퓨터를 일컫는다.

SSE (Streaming SIMD Extensions)는 이런 SIMD 구조의 명령을 프로세서에 도입한 것으로 128bit짜리 SIMD 전용 레지스터가 추가된 것이다. 

 

 

 

 

 

◎ 사용

코드에서 이 라이브러리를 사용하려면 헤더파일 xnamath.h를 추가하기만 하면 된다.

#include <xnamath.h>

모든 코드가 헤더 파일 안에 inline으로 구현되어 있으므로 라이브러리 파일을 따로 링크할 필요가 없다. 

물론 <d3dx10.h>를 포함시키고 <d3dx10.lib>를 링크해서 XNA Math 라이브러리 대신에 D3DX10 Math 함수들을 사용하는 것도 여전히 가능하다. 

 

 

 

 

 

◎ 벡터 형식들

XNA Math에서 핵심 벡터 형식은 SIMD 하드웨어 레지스터들에 대응되는 XMVECTOR이다.

XMVECTOR는 하드웨어 레지스터에 대응하는 32-비트의 실수 벡터로 32-비트 실수 또는 정수 요소들의 벡터를 표현하기 위한 '자료형'이다. (구조체가 아니다.) 

추가로 덧붙이자면 뒤에 나오는 XMMATRIX는 구조체이다.

 

XMVECTOR는 128비트짜리 형식으로 하나의 SIMD 명령으로 처리되는 네 개의 32비트 부동소수점 값들로 이루어져있다.

SSE2를 사용할 수 있는 경우에서 XMVECTOR는

typedef __m128 XMVECTOR;

위와 같이 정의되어 있다.

 

__m128?

더보기

특별한 SIMD 형식으로 계산을 수행할 때 이 형식의 벡터들은 반드시 SIMD의 장점을 갖고 연산을 할 수 있다. 

이 XMVECTOR는 사용할 때 16바이트 경계에 정렬되어야 한다.

 

왜냐하면 정렬이 되어있어야 SIMD 명령어를 이용해 데이터를 벡터에 패킹하여 한 번의 명령으로 동시에 처리할 수 있기 때문이다.

이 정렬은 지역 변수와 전역 변수에는 정렬이 자동으로 이루어지게 된다.

그러나  class의 멤버나 구조체의 멤버변수의 경우에는 이것 보다는 XMFLOAT를 사용하는 것이 더 바람직하다. 

 

 

 

왜?

더보기

(Microsoft Docs의 DirectXMath 中)

SIMD 처리는 데이터를 SIMD 레지스터에 load하고 결과를 추출하기 전에 완전히 처리할 때 가장 효율적이다.

SIMD processing is most efficient when data is loaded into the SIMD registers and fully processed before extracting the results. 

스칼라와 벡터 형식 간의 변환은 비효율적이므로 필요할 때만 수행하는 것이 좋다.

Conversion between scalar and vector forms is inefficient, so we recommend that you do it only when required. 

이러한 이유로 스칼라 값을 생성하는 DirectXMath 라이브러리의 함수는 스칼라 결과가 결과 벡터에 복제되는 벡터 형태로 리턴된다. 

For this reason, functions in the DirectXMath library that produce a scalar value are returned in a vector form where the scalar result is replicated across the resulting vector

 

그래서 스칼라 값이 필요한 경우에 이와 같은 내용을 수행하는 방법에 대한 몇 가지 선택사항으로 XMFLOAT를 이용해 벡터를 메모리 구조에 저장하고 다시 읽어오는 방식을 사용. 

 

 

 

typedef struct _XMFLOAT3 
{
	FLOAT x;
	FLOAT y;
	FLOAT z;
} XMFLOAT3;

예를 들면 XMFLOAT3(3차원)은 위와 같이 정의한 후 사용한다. 

XMVECTOR나 XMMATRIX는 operator(연산자)들이 오버로드 되어있지만 XMFLOAT를 이용해 사용하는 경우 추가로 생성자들이나 연산자 관련 함수를 추가해 준다. 

 

이 XMFLOAT를 이용한 형식은 계산할 때 SIMD의 장점을 갖고 연산할 수가 없으므로 SIMD를 이용하고 싶으면 이 형식의 인스턴스를 XMVECTOR 형식으로 변환해줘야 한다. 

 

이를 위해 XNA Math는 여러 함수를 제공한다.

또한 XNA Math는 XMVECTOR 인스턴스에 담긴 자료를 XMFLOAT* 형식들로 변환하는 저장 함수들도 제공한다.

 

 

 

간략정리

더보기

간략정리

1. 지역 변수와 전역 변수에서는 XMVECTOR를 이용

2. 클래스 자료 멤버에는 XMFLOAT2나 XMFLOAT3, XMFLOAT4를 사용

3. 계산 수행 전 함수들을 이용해 XMFLOAT*를 XMVECTOR로 변환

4. '계산'은 XMVECTOR 인스턴스들을 이용해서 수행

5. XMVECTOR를 XMFLOAT*로 변환하려면 저장 함수들을 이용

 

 

 

 

 

◎ 적재 및 저장 함수

- XMFLOAT*의 자료를 XMVECTOR로 적재(Load)하는 함수

XMVECTOR XMLoadFloat2 (Const XMFLOAT2 *pSource);

XMVECTOR XMLoadFloat3 (Const XMFLOAT3 *pSource);

XMVECTOR XMLoadFloat4 (Const XMFLOAT4 *pSource);

이외에도 Color나 UINT 배열을 XMVECTOR에 load하는 함수들도 존재한다.

 

 

- XMVECTOR의 자료를 XMFLOAT*로 저장(Store)하는 함수

FLOAT XMVectorGetX (FXMVECTOR V);
FLOAT XMVectorGetY (FXMVECTOR V);
FLOAT XMVectorGetZ (FXMVECTOR V);
FLOAT XMVectorGetW (FXMVECTOR V);

XMVECTOR XMVectorSetX (FXMVECTOR V, FLOAT x);
XMVECTOR XMVectorSetY (FXMVECTOR V, FLOAT y);
XMVECTOR XMVectorSetZ (FXMVECTOR V, FLOAT z);
XMVECTOR XMVectorSetW (FXMVECTOR V, FLOAT w);

 

 

 

 

 

◎ 매개변수 전달과 상수 벡터 

SIMD의 장점을 이용하려면 함수에 XMVECTOR 형식의 매개변수를 전달할 때 지켜야 할 규칙이 있다.

이 규칙들은 플랫폼바다 다른데 플랫폼 독립적 코드를 위해 XMVECOTR 형식의 매개변수에 대해 CXMVECTOR, FXMVECTOR 형식을 사용한다.

 

Windows의 경우

// 32비트 Windows
typedef const XMVECTOR FXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;

//64비트 Windows
typedef const XMVECTOR& FXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;

 

이 둘의 차이는 XMVECTOR의 복사본을 직접 전달할 수 있는지, 참조를 전달해야 하는지이다.

구체적인 또 하나의 규칙은 한 함수의 처음 세 개의 XMVECTOR 매개변수는 반드시 FXMVECTOR형식이어야 한다. 그 외의 XMVECTOR 매개변수는 반드시 CXMVECOTR 형식이여야 한다. 

 

예를 들어 7개의 매개변수를 받는 함수가 있으면 위의 규칙에 따라 처음 세 개는 FXMVECTOR, 나머지 네 개는 CXMVECOTR 형식이어야 하며 XMVECTOR 매개변수들 사이에 다른 매개변수가 끼어있을 경우에도 처음 세 개의 XMVECTOR 매개변수는 반드시 FXMVECTOR 형식이고 나머지 XMVECTOR 매개변수는 CXMVECOTR 형식이어야 한다. 

 

 

또한 상수(const) XMVECTOR 인스턴스에는 반드시 XMVECTORF32 형식을 사용해야 한다.

초기화 구문을 사용하고자 할 때에는 항상 XMVECTORF32를 사용해야 한다.

XMVECTORF32는 16바이트 경계로 정렬된 구조체로, XMVECTOR로의 변환 연산자를 지원한다. 

 

 

 

 

 

◎ 기타 제공 함수

XNA Math 라이브러리는 XMVECTOR를 이용한 벡터 덧셈, 뺄셈, 스칼라 곱셈을 위해 중복적재된 연산자들을 제공한다.

또한 XNA Math 라이브러리는 벡터의 길이, 벡터 길이의 제곱, 두 벡터의 내적, 두 벡터의 외적, 벡터 정규화를 위한 편의용 함수들도 제공한다.

 

XMVECTOR XMVector3Length(FXMVECTOR V);
XMVECTOR XMVector3LengthSq(FXMVECTOR V);
XMVECTOR XMVector3Dot (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR XMVector3Cross (FXMVECTOR V1, FXMVECTOR V2);
XMVECTOR XMVector3Normalize (FXMVECTOR V);

 

 

 

 

 

 

 

 

 

참고 출처;


https://docs.microsoft.com/ko-kr/windows/win32/dxmath/pg-xnamath-getting-started?redirectedfrom=MSDN#type_usage_guidelines_

 

 

Getting started (DirectXMath) - Win32 apps

Getting started (DirectXMath) In this article --> The DirectXMath Library implements an optimal and portable interface for arithmetic and linear algebra operations on single-precision floating-point vectors (2D, 3D, and 4D) or matrices (3×3 and 4×4). The l

docs.microsoft.com


- https://ko.wikipedia.org/wiki/SIMD

 

SIMD - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 둘러보기로 가기 검색하러 가기

ko.wikipedia.org


- Direct11을 이용한 3D게임프로그래밍 (저; 프랭크 D. 루나, 역; 류광, 출판사; 한빛미디어)


 

 

 

벡터(Vector) 下

 

 

 

 

◎벡터의 크기

기하학적으로 한 벡터의 크기는 해당 방향이 있는 선분의 길이이다.

벡터의 크기는 이중 수직선으로 표기한다.

Ex ) 벡터 u의 크기는 ||u||이다. 

 

3차원 벡터의 크기는 피타고라스의 정리를 두 번 적용해 구할 수 있다.

 

어떻게 ||u||가 저 식이 될까?

더보기

우선 u의 크기를 빗변으로, y축을 높이 y로 두고 원점으로부터 y까지 직각이 되는 거리를 a라고 둔다.

그러면 ||u|| (빗변) 은 피타고라스의 정리를 통해 y제곱과 a제곱에 루트를 씌운 값이 된다. 

그리고 거기서 a를 구하기 위해 피타고라스의 정리를 한 번 더 사용한다.

a를 빗변으로 두고 x를 밑변, z축의 값을 높이 z로 두어 계산한다.

그러면 ||u||가 위와 같은 식이 된다. 

 

 

벡터를 순수하게 방향을 나타내는 용도로만 사용하면 벡터의 길이는 그다지 중요하지 않을 수 있다.

그런 방향 전용 벡터에서는 벡터의 길이를 정확히 단위 길이인 1로 맞추어 두는 것이 편리하다. 

따라서 벡터의 길이를 단위 길이가 되게 해서 단위 벡터로 만드는 것을 가리켜 벡터의 정규화(normalization)라고 부른다.

벡터의 각 성분을 벡터의 크기로 나누면 벡터가 정규화 된다. 

 

벡터의 정규화

왜 벡터의 크기로 각 성분을 나누는지 이해가 되지 않는다면 이렇게 생각하면 쉽다. 

 

 

 

 

벡터의 곱셈에는 두 가지 종류가 있다. 내적과 외적이다.

 내적

스칼라 값을 나타내는 벡터 곱셈의 일종으로 점곱(dot product)라고도 한다.

 

내적은 결과 값이 뒤에 나올 외적의 결과와 달리 스칼라 값으로 나온다.

 

u = (u1, u2, u3), v = (v1, v2, v3)라고 가정했을 때 내적은

u · v = u1v1 + u2v2 + u3v3

즉, 대응되는 성분들의 곱들의 합이다.

 

 

코사인 법칙을 이용하면 내적의 기하학적 의미를 알아낼 수 있다.

u · v = ||u|| ||v|| cosθ

여기서 θ는 벡터 u와 v 사이의 각도이다. 0 θ π 사이의 값.

따라서 위의 식은 두 벡터의 내적이 두 벡터 사이의 각도의 코사인을 벡터 크기들로 비례 시킨 것이다. 

특히 u와 v가 둘 다 단위 벡터이면 u, v의 내적은 두 벡터 사이의 각도의 코사인이다. 

u · v = 1 * 1 * cosθ = cosθ

 

1. u·v = 0 이면 u ⊥ v이다. ( = 두 벡터는 직교한다. )

2. u·v > 0 이면 두 벡터 사이의 각도 θ는 90˚보다 작다. ( = 두 벡터는 예각을 이룬다.)

3. u·v < 0 이면 두 벡터 사이의 각도 θ90˚보다 크다. ( = 두 벡터는 둔각을 이룬다. )

 

 

 

 

 외적

결과 값이 스칼라 값을 나타내는 내적과는 달리 외적의 결과는 또 다른 벡터가위곱(cross product)라고도 한다. 

 

외적은 오직 3차원 벡터에 대해서만 정의된다.

두 3차원 벡터 u와 v의 외적을 구하면 u와 v 모두에 직교(수직)인 또 다른 벡터 w가 나온다.

 

즉, w는 u와 직교이고 v와도 직교이다.

 

 

u = (u1, u2, u3), v = (v1, v2, v3)라고 가정했을 때 외적은 다음과 같이 구한다. 

w = u x v = (u2v3 - u3v2, u3v1 - u1v3, u1v2 - u2v1) 

w = u x v 

 

 

 

 

 

 

 

 

참고 출처

Direct11을 이용한 3D게임프로그래밍 (저; 프랭크 D. 루나, 역; 류광, 출판사; 한빛미디어)

 

+ Recent posts