`I` 매크로 — 복소수·허수 단위 상수

I 매크로 — 복소수·허수 단위 상수

복소수 코드를 짜다 보면 I가 어디서나 등장해요. 1.0 + 2.0*I 같은 식으로 허수부를 붙이는 게 C에서 복소수를 만드는 가장 익숙한 방법이죠. 이 I가 정확히 무엇으로 확장되는지, 그리고 왜 수학에서 쓰는 i가 아니라 대문자 I인지 살펴볼게요.

출처: cppreference — I

본문

I 매크로는 _Complex_I 또는 _Imaginary_I 중 하나로 확장돼요. 구현이 허수 타입을 지원하지 않으면 항상 _Complex_I로 확장되죠.

#define I /* unspecified */   // (since C99)

A program may #undef and perhaps then redefine the macro I. 프로그램은 I 매크로를 #undef하고, 원하면 다시 정의할 수도 있어요.

Notes

수학에서는 허수 단위를 소문자 i로 쓰는데, C에서는 왜 대문자 I일까요? 이유는 간단해요. 소문자 i는 아주 오래전부터 많은 C 프로그램에서 루프 카운터 변수로 쓰여 왔기 때문이에요. 이름이 이미 널리 점유되어 있어서 대문자 I를 쓰기로 한 거예요.

I는 보통 x + y*I 같은 표현으로 복소수를 만들 때 써요. 그런데 I_Complex_I로 정의되어 있다면, 이런 표현이 y-0.0일 때도 허수부를 +0.0으로 만들어버릴 수 있어요. 가지 자름(branch cut)이 있는 복소 함수에는 이 부호 차이가 중요하죠. 이때 CMPLX 매크로가 복소수를 정확히 구성하는 방법을 제공해요.

GCC는 비표준 확장으로, 정수 리터럴에 i 접미사를 붙여 허수 상수를 지정하게 해줘요. 1.0fi, 1.0i, 1.0li가 GNU C에서 허수 단위예요. 비슷한 방식이 C++14부터는 표준 C++의 일부가 됐어요(C++에서는 1.0if, 1.0i, 1.0il이 허수 단위).

예시

I로 복소수를 만들고 허수 단위의 성질을 확인해 보는 코드예요.

#include <stdio.h>
#include <complex.h>

int main(void)
{
    printf("I = %.1f%+.1fi\n", creal(I), cimag(I));

    double complex z1 = I * I;     // imaginary unit squared
    printf("I * I = %.1f%+.1fi\n", creal(z1), cimag(z1));

    double complex z = 1.0 + 2.0*I; // usual way to form a complex number pre-C11
    printf("z = %.1f%+.1fi\n", creal(z), cimag(z));
}

출력:

I = 0.0+1.0i
I * I = -1.0+0.0i
z = 1.0+2.0i

I * I-1.0+0.0i인 걸로 허수 단위의 정의가 코드에서 그대로 재현되는 걸 확인할 수 있어요.

표준 참조

  • C11: 7.3.1/6 I (p: 188), G.6/1 I (p: 537)
  • C99: 7.3.1/4 I (p: 170), G.6/1 I (p: 472)

더 알아보기

  • I가 확장되는 두 후보 _Complex_I_Imaginary_I가 어떻게 다른지 살펴보면 I의 동작이 완전히 이해돼요.
  • 영 부호를 정확히 보존해야 한다면 CMPLX 매크로가 I의 대안이 돼요.
  • cppreference의 I 원문에서 참조 절을 더 확인할 수 있어요.