C++/C++

2진수를 10진수로 변환하기

Elan 2021. 11. 22. 06:02

10진수를 이용하여 표현된 2진 상수의 값을 계산하는 템플릿을 만들어보자.

 

 

우선 일반 함수 재귀를 이용하여 만들어 보자.

 

 

일반 함수로 구현

 

int Binary(int num) {
	if (num == 0)
		return 0;
	return Binary(num / 10) << 1 | (num % 10);
}

아래 코드(더보기)를 실행하며 진행을 관찰해보면 어떻게 동작하는지를 알 수 있다.

 

더보기

 

int Binary(int num, int tab = 1) {
	if (num == 0) {
		std::cout << "num = 0 (재귀 종료)" << std::endl << std::endl;
		return 0;
	}

	std::cout << "num = " << num << std::endl;
	for (size_t i = 0; i < tab*2; i++)	std::cout << "\t";
	std::cout << "재귀 " << tab << "  ";


	int sum1 = Binary(num / 10, tab + 1) << 1;
	
	for (size_t i = 0; i < (tab-1)*2; i++)	std::cout << "\t";
	std::cout << "(" << num << " / 10) << 1 = " << sum1 << std::endl;
	
	int sum2 = num % 10;

	for (size_t i = 0; i < (tab - 1) * 2; i++)	std::cout << "\t";
	std::cout << num << "% 10  =  " << sum2 << std::endl;

	for (size_t i = 0; i < (tab - 1) * 2; i++)	std::cout << "\t";
	std::cout << sum1 << " | " << sum2 << " = " << (sum1 | sum2) << std::endl;
	for (size_t i = 0; i < (tab - 1) * 2; i++)	std::cout << "\t";
	std::cout << "재귀 탈출" << std::endl << std::endl;

	return sum1 | sum2;
}

int main(){
	int res = Binary(11101);
    std::cout << res;
}

 

&amp;amp;amp;amp;lt; 출력 결과 &amp;amp;amp;amp;gt;

재귀 프레임을 탈출하는 과정에서  일어나는 변화를 잘 관찰해보면 답을 알 수 있다.

▲▲▲

 

 

템플릿 코드

template<unsigned long num>
struct binary
{
	static unsigned const value =
		binary<num / 10>::value << 1 | num % 10;
};

// Specialization for zero
template<>
struct binary<0>
{
	static unsigned const value = 0;
};

int main()
{
	int res = binary<11101>::value; // 29
}

 

템플릿 코드에서 주의해서 봐야할 부분은 다음과 같다.

 value = binary<num / 10>::value <<1 | num % 10;

그 중에서도  표현식 뒷부분의 num % 10 을 눈여겨 봐야한다.

 

 

아래 그림은 10진수 11을 2진수 1011로 변환하는 과정이다.

&amp;amp;amp;amp;lt;10진수가 2진수로 변환되는 과정&amp;amp;amp;amp;gt;

과정을 나열해보면 다음과 같다.

  •   주어진 수를 2로 나눈다. 
  •   몫과 나머지를 구한다.
  •   몫이 1이 될때까지 반복한다.
  •   각 나머지들을 역순으로 나열하여 2진수를 만든다.

11을 2로 나누면 몫은 5이고 이때 나머지는 1 이다. 이 때 나머지는 2진수의 첫째자리이다.

5를 2로 나누면 몫은 2이고 이때 나머지는 1 이다. 이 때 나머지는 2진수의 둘째자리이다.

2를 2로 나누면 몫은 1이고 이때 나머지는 0 이다. 이 때 나머지는 2진수의 셋째자리이다.

몫이 2보다 작아졌으므로 이것은 2진수의 가장 높은 자리가 된다.

 

구한 값들을 위 그림의 화살표 방향으로 나열하면 1011 이 된다.

이것이 10진수 11을 이진수로 변환한 값이다.

 

 

위의 과정의 역순으로 진행하면 2진수를 10진수로 변환하는 과정이 된다.

 

 

 

템플릿이 재귀하며 계산되는 과정을 살펴보자

 

 

1) 최초 템플릿 호출 코드에서 29에 해당하는 11101을 전달 binary<11101>::value

int res = binary<11101>::value; // 29

 

 

 

 

 

2) binary<11101>::value 에 대한 최초의 템플릿 추론 시작

// num = 11101
value =	binary<num / 10>::value << 1 | num % 10;

위 코드에서      binary<11101 / 10>::value << 1     부분에 대한 추론이 필요.

 

11101 / 10 = 1110 이되므로 binary<1110>::value << 1 에 대한 추론 시작.

 

 

 

 

 

3) binary<1110>::value << 1 에 대한 템플릿 재귀 추론

// num = 1110
value =	binary<num / 10>::value << 1 | num % 10;

위 코드에서      binary<1110 / 10>::value << 1     부분에 대한 추론이 필요.

 

1110 / 10 = 111 이되므로 binary<1110>::value << 1 에 대한 추론 시작.

 

 

 

 

 

4) binary<111>::value << 1 에 대한 템플릿 재귀 추론

 

// num = 111
value =	binary<num / 10>::value << 1 | num % 10;

위 코드에서      binary<111 / 10>::value << 1     부분에 대한 추론이 필요.

 

111 / 10 = 11 이되므로 binary<11>::value << 1 에 대한 추론 시작.

 

 

 

 

 

5) binary<11>::value << 1 에 대한 템플릿 재귀 추론

// num = 11
value =	binary<num / 10>::value << 1 | num % 10;

위 코드에서      binary<11 / 10>::value << 1     부분에 대한 추론이 필요.

 

11 / 10 = 1 이되므로 binary<1>::value << 1 에 대한 추론 시작.

 

 

 

 

6) binary<1>::value << 1 에 대한 템플릿 재귀 추론

// num = 1
value =	binary<num / 10>::value << 1 | num % 10;

위 코드에서      binary<1 / 10>::value << 1     부분에 대한 추론이 필요.

 

1 / 10 = 0 이되므로 binary<0>::value << 1 에 대한 추론 시작.

 

 

 

 

 

7) binary<0>::value << 1 에 대한 템플릿 재귀 추론

// num = 0
value =	binary<num / 10>::value << 1 | num % 10;

위 코드에서      binary<0 / 10>::value << 1     부분에 대한 추론이 필요.

 

1 / 10 = 0 이되므로 binary<0>::value에 대한 추론 결과는

아래의 템플릿이 선택되면서 0을 반환하고, 재귀 프레임은 더 이상 늘어나지 않는다.

template<>
struct binary<0>
{
	static unsigned const value = 0;
};

그리고 0<<1 | 0 % 10 의 결과인 0을 상위 호출 프레임에 반환한다.(현재 재귀 프레임 탈출)

 

 

 

 

 

8)  binary<1>::value << 1  |  num % 10 에 대한 템플릿 재귀 추론

// num = 1
value =	binary<num / 10>::value << 1 | num % 10;

binary<num / 10>::value << 1 | num % 10 부분에서 

binary<1 / 10>::value << 1 에 대한 결과는 0임을 확인하였다.

 

이제 나머지 num % 10 부분에 대한 결과만 확인하면 된다.

 

num이 1이므로 1 % 10 = 1 이 된다.

따라서 0 | 1 의 결과는 1이된다.

 

이 결과를 상위 호출 프레임에 반환한다.

 

 

 

 

 

9)  binary<11>::value << 1  |  num % 10 에 대한 템플릿 재귀 추론

// num = 11
value =	binary<num / 10>::value << 1 | num % 10;

binary<num / 10>::value << 1 | num % 10 부분에서 

binary<11 / 10>::value는 1이므로 1 << 1 = 2 가 된다.

 

이제 나머지 num % 10 부분에 대한 결과만 확인하면 된다.

 

num이 11이므로 11 % 10 = 1 이 된다.

따라서 2 | 1 의 결과는 3이된다. ( 이진수 연산으로 표현 ->  10 | 01 = 11 )

 

이 결과를 상위 호출 프레임에 반환한다.

 

 

 

 

 

10)  binary<111>::value << 1  |  num % 10 에 대한 템플릿 재귀 추론

// num = 111
value =	binary<num / 10>::value << 1 | num % 10;

binary<num / 10>::value << 1 | num % 10 부분에서 

binary<111 / 10>::value는 3이므로 3 << 1 = 6 이 된다.

 

이제 나머지 num % 10 부분에 대한 결과만 확인하면 된다.

 

num이 111이므로 111 % 10 = 1 이 된다.

따라서 6 | 1 의 결과는 3이된다. ( 이진수 연산으로 표현 ->  110 | 001 = 111 )

 

이 결과를 상위 호출 프레임에 반환한다.

 

 

 

 

 

11)  binary<1110>::value << 1  |  num % 10 에 대한 템플릿 재귀 추론

// num = 1110
value =	binary<num / 10>::value << 1 | num % 10;

binary<num / 10>::value << 1 | num % 10 부분에서 

binary<1110 / 10>::value는 7이므로 7 << 1 = 14 가 된다.

 

이제 나머지 num % 10 부분에 대한 결과만 확인하면 된다.

 

num이 1110이므로 1110 % 10 = 0 이 된다.

따라서 14 | 0 의 결과는 14이된다. ( 이진수 연산으로 표현 ->  1110 | 0000 = 1110 )

 

이 결과를 상위 호출 프레임에 반환한다.

 

 

 

 

12)  binary<11101>::value << 1  |  num % 10 에 대한 템플릿 재귀 추론

// num = 11101
value =	binary<num / 10>::value << 1 | num % 10;

binary<num / 10>::value << 1 | num % 10 부분에서 

binary<11101 / 10>::value는 14 이므로 14 << 1 = 28 이 된다.

 

이제 나머지 num % 10 부분에 대한 결과만 확인하면 된다.

 

num이 11101이므로 11101 % 10 = 1 이 된다.

따라서 28 | 1 의 결과는 29 가 된다. ( 이진수 연산으로 표현 ->  11100 | 00001 = 11101 )

 

이 결과를 상위 호출 프레임에 반환하면

int res = 29; 가 된다.

 

이 모든 과정이 컴파일 타임에 완료되므로

프로그램 실행 시 런타임에서는 아무런 계산비용이 발생하지 않게 된다.

 

 

 

재귀 과정 요약

최초 호출 프레임

binary<11101 / 10>::value << 1 | 11101 % 10;

29에 해당하는 11101을 num으로 전달하여 호출




             1번째 재귀

             binary<1110 / 10>::value << 1 | 1110 % 10;

             num / 10 = 1110 로 재귀.


             
                           2번째 재귀

                            binary<111>::value << 1 | 111 % 10;

                            num / 10 = 111 로 재귀.



                                          3번째재귀

                                          binary<11>::value << 1 | 11 % 10;

                                          num / 10 = 11 로 재귀.




                                                        4번째재귀

                                                        binary<1>::value << 1 | 1 % 10;

                                                        num / 10 = 1 로 재귀.




                                                                      5번째재귀

                                                                      binary<0>::value << 1 | 0 % 10;

                                                                      binary<0>::value = 0 이므로 재귀를 탈출하여 4번째 재귀로 이동



                                                         4번째 재귀

                                                         (1/10) << 1  |  1 % 10 = 1 이 되며, 재귀를 탈출하여3번째 재귀
로 이동


                                          3번째 재귀

                                          1 << 1  |  11 % 10 = 3이 되며, 재귀를 탈출하여2번째 재귀로 이동



                           2번째 재귀
                           3 << 1  | 111 % 10 = 7이 되며, 재귀를 탈출하여1번째 재귀로 이동


             1번째 재귀

             7 << 1  | 1110 % 10 = 14가 되며,재귀를 탈출하여 최초 호출 프레임로 이동


최초 호출 프레임
14 << 1 |  11101 % 10 = 29가 되며,  템플릿 추론 종료.

간단히 정리하면 다음과 같다.

  • 재귀 추론 탈출 조건인 num / 10 == 0을 만족하여 템플릿 binary<0>을 만날 때까지 재귀가 중첩된다.
  • 재귀 탈출 조건을 만나면 ( num / 10 ) << 1 | num % 10 의 결과를 상위 호출 프레임으로 전달해준다.
  • 템플릿 추론 프레임들이 하나씩 해제되면서
    최초 호출 프레임에 도달하면 ( num / 10 ) << 1 | num % 10 의 결과를 최종적으로 계산한다.