CORDIC 알고리즘 분석: 시프트와 덧셈 연산만으로 sin() 함수 구현하기

FPU(부동소수점 연산 장치)가 탑재되지 않은 마이크로컨트롤러 환경에서 삼각함수를 실시간으로 계산하는 것은 까다로운 과제입니다. 룩업 테이블(LUT) 방식은 메모리 사용량과 정밀도 사이의 트레이드오프가 발생하며, 테일러 급수 전개는 과도한 곱셈과 나눗셈 연산으로 인해 성능 병목을 유발합니다. 이러한 하드웨어 제약 사항 속에서 CORDIC(Coordinate Rotation Digital Computer) 알고리즘은 복잡한 곱셈 연산을 단순한 비트 시프트와 덧셈으로 대체하여 효율적인 해결책을 제시합니다.

벡터 회전을 통한 각도 근사

CORDIC의 핵심 메커니즘은 반복적 접근에 기반합니다. 목표 각도로 한 번에 회전하는 대신, 미리 정의된 일련의 각도를 반복적으로 더하거나 빼면서 점진적으로 목표치에 수렴합니다. 각 단계에서 회전각은 이전 단계의 약 절반 크기를 가지며, 특히 탄젠트 값이 2의 음의 정수 거듭제곱($2^{-i}$)이 되도록 설계됩니다.

일반적인 2차원 벡터 회전 공식은 다음과 같습니다.

x' = x * cos(θ) - y * sin(θ)
y' = y * cos(θ) + x * sin(θ)

이 식을 $\cos(\theta)$로 묶어 재구성하면 다음과 같은 형태가 됩니다.

x' = cos(θ) * [x - y * tan(θ)]
y' = cos(θ) * [y + x * tan(θ)]

여기서 회전각 $\theta_i$를 $\tan(\theta_i) = 2^{-i}$가 되도록 제한하면, $\tan(\theta_i)$를 곱하는 연산은 하드웨어 친화적인 우측 비트 시프트 연산으로 대체됩니다. $\cos(\theta)$에 의한 길이 스케일링은 반복문이 끝난 후 미리 계산된 상수를 한 번 곱해주는 것으로 처리할 수 있습니다. 결과적으로 반복문 내부의 곱셈은 완전히 사라지고 덧셈, 뺄셈, 시프트 연산만 남게 됩니다.

x_next = x - (y >> i) * direction
y_next = y + (x >> i) * direction

위 수식에서 >> ii비트만큼 우측으로 시프트함을 의미하며, direction은 현재 누적 각도와 목표 각도의 차이에 따라 결정되는 회전 방향(+1 또는 -1)입니다.

고정소수점을 활용한 C언어 구현

아래 코드는 하드웨어 자원이 제한된 환경을 고려하여 부동소수점 대신 고정소수점(Fixed-point) 연산을 사용하도록 재설계된 CORDIC 사인 함수 구현체입니다. 비트 시프트 연산을 직접 활용하여 곱셈을 배제했습니다.

#include <stdint.h>

#define CORDIC_STEPS 15
#define FRAC_BITS 16
#define ONE_FIXED (1 << FRAC_BITS)

// atan(2^-i) 값을 고정소수점 포맷으로 변환한 룩업 테이블
const int32_t angle_lut[CORDIC_STEPS] = {
    29491, 17407, 9198, 4669, 2343, 1173, 586, 293, 
    147, 73, 37, 18, 9, 5, 2
};

int32_t compute_sine_cordic(int32_t target_angle) {
    // 초기 x좌표는 K * ONE (K ≈ 0.60725, CORDIC 이득 보정 계수)
    int32_t coord_x = 39797; 
    int32_t coord_y = 0;
    int32_t accum_angle = 0;

    for (int i = 0; i < CORDIC_STEPS; i++) {
        int32_t shifted_x = coord_x >> i;
        int32_t shifted_y = coord_y >> i;
        int32_t temp_x, temp_y;

        if (accum_angle < target_angle) {
            temp_x = coord_x - shifted_y;
            temp_y = coord_y + shifted_x;
            accum_angle += angle_lut[i];
        } else {
            temp_x = coord_x + shifted_y;
            temp_y = coord_y - shifted_x;
            accum_angle -= angle_lut[i];
        }
        coord_x = temp_x;
        coord_y = temp_y;
    }
    
    // 최종 y좌표가 sin 값에 해당
    return coord_y; 
}

반복 단계별 회전 파라미터

CORDIC 알고리즘이 수렴해 나가는 과정을 이해하기 위해, 초기 반복 단계에서 사용되는 주요 파라미터를 정리한 테이블은 다음과 같습니다.

반복 단계 (i) 회전 각도 ($\theta_i$) 탄젠트 값 ($\tan \theta_i$) 시프트 연산 ($2^{-i}$)
0 45.000° 1.0 >> 0
1 26.565° 0.5 >> 1
2 14.036° 0.25 >> 2
3 7.125° 0.125 >> 3

태그: CORDIC 삼각함수 임베디드 C언어 고정소수점

7월 16일 19:09에 게시됨