Menu

C 언어 수학 함수: math.h, sqrt, pow, 그리고 -lm 링크

math.h 둘러보기 - sqrt, pow, fabs, floor와 ceil, round, fmod, 삼각함수와 로그 계열, INFINITY와 NAN - 그리고 고전적인 undefined reference 오류를 일으키는 -lm 링커 플래그.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

C의 핵심 언어는 + - * /%만 줄 뿐 그 밖에는 아무것도 주지 않습니다. 거듭제곱 연산자도, 제곱근도, 반올림도 없습니다. 산술을 넘어서는 모든 것은 <math.h>에 선언된 표준 수학 라이브러리에 있습니다.

이 함수들은 모두 double 정밀도로 동작합니다. double 인자를 받고 double을 반환합니다. 가장 먼저 몸에 익혀야 할 사실인데, int를 넘기면 조용히(변환되어) 동작하는 반면 결과를 int에 대입하면 소수 부분이 조용히 버려지기 때문입니다.

시작하기, 그리고 -lm 플래그

자기 컴퓨터에서는 이것을 컴파일할 때 실패할 수 있습니다.

/usr/bin/ld: /tmp/ccXYZ.o: in function `main':
program.c:(.text+0x1a): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status

math.h에서 가장 흔한 걸림돌이고, 여러분 코드의 실수가 아닙니다. 헤더가 선언을 줬으니 컴파일러는 만족했지만, 구현은 리눅스에서 링커가 기본으로 포함하지 않는 별도 라이브러리(libm)에 있습니다. -lm을 붙이세요.

gcc program.c -o program -lm

플래그는 소스 파일 에 와야 합니다. 링커는 입력을 순서대로 처리하므로, 그것을 필요로 하는 코드보다 앞에 나열된 라이브러리는 아무것도 해결해 주지 않습니다. macOS와 윈도우의 MinGW에서는 수학 루틴이 이미 표준 C 라이브러리의 일부라 -lm이 필요 없습니다(macOS에서는 붙여도 무해합니다).

거듭제곱과 근

음수의 sqrt(x)는 죽는 대신 NaN을 반환합니다. 인자가 음수일 수 있다면 지키세요. hypot은 알아둘 만한데, sqrt(x*x + y*y)와 같은 값을 계산하면서 xy가 아주 클 때도 넘치지 않기 때문입니다.

작은 정수 거듭제곱에는 단순한 곱셈이 더 빠르고 정확합니다. x * xpow(x, 2.0)을 이깁니다. 후자는 로그를 거치므로 9를 기대한 자리에 8.999999999999998을 돌려줄 수 있습니다.

반올림: floor, ceil, round, trunc

함수 넷, 음수에 대해 서로 다른 답 넷입니다. 그 표가 외워 둘 만한 부분입니다.

  • floor(x) - x보다 크지 않은 가장 큰 정수. 항상 음의 무한대 쪽으로 움직입니다. floor(-2.3)-3입니다.
  • ceil(x) - x보다 작지 않은 가장 작은 정수. 항상 양의 무한대 쪽으로 움직입니다. ceil(-2.3)-2입니다.
  • round(x) - 가장 가까운 정수, 0.5는 0에서 멀어지는 쪽으로. round(2.5)3, round(-2.5)-3입니다.
  • trunc(x) - 소수 부분을 잘라 0 쪽으로 움직입니다. trunc(-2.7)-2입니다.

넷 다 double을 반환합니다. 정수가 필요하면 int로 캐스팅해도 되지만, (int)x 자체가 잘라내기를 수행한다는 점을 알아 두세요. 그래서 옛날의 "반올림" 관용구 (int)(x + 0.5)는 음수에 대해 틀립니다. round()를 쓰세요.

fabs와 fmod

fabs는 부동소수점의 절댓값입니다. 대신 abs에 손대는 것이 고전적인 실수인데, absint를 받고 변환이 조용히 일어나기 때문입니다.

%double에 아예 동작하지 않습니다. 정수 연산자라서 컴파일러가 거부합니다. fmod(a, b)가 그 부동소수점 대응물이고, a의 부호를 유지합니다.

fabs는 double 둘을 비교하는 올바른 방법이기도 합니다. 부동소수점에 ==를 쓰는 것은 함정이니까요.

0.10.2는 이진수로 정확히 표현할 수 없으므로, 그 합은 0.3에서 머리카락 하나만큼 떨어져 있습니다. ==가 아니라 허용 오차로 비교하세요.

삼각함수

삼각함수는 도가 아니라 라디안으로 동작합니다. 틀린 답의 또 다른 확실한 근원입니다.

전체 목록은 이렇습니다. sin, cos, tan, 그 역함수인 asin, acos, atan, 인자 둘짜리 atan2(y, x), 그리고 쌍곡선 계열 sinh, cosh, tanh입니다. 각도에는 atan(y / x)보다 atan2(y, x)를 선호하세요. x == 0을 처리하고 사분면을 제대로 맞춥니다.

math.h는 대부분의 시스템에서 M_PI를 정의하지만 표준 C가 아니라 POSIX 확장이므로 -std=c17 아래에서는 없을 수 있습니다. 위처럼 자기 상수를 정의하는 편이 이식성이 있습니다.

로그와 지수

수학에서 넘어온 이름의 함정을 보세요. log()는 밑이 10이 아니라 자연로그(밑이 e)입니다. 밑 10은 log10()입니다.

log(0.0)은 음의 무한대를, log(-1.0)은 NaN을 반환합니다. 둘 다 죽지 않으므로, 잘못된 인자가 무언가 nan을 출력할 때까지 계산을 조용히 타고 흐릅니다.

INFINITY, NAN, 그리고 그것들 확인하기

부동소수점에는 숫자가 아닌 값이 있고, math.h가 그것들에 이름을 붙입니다.

마지막 줄이 핵심입니다. if (x == NAN)이라고 쓸 수 없습니다. 항상 거짓입니다. isnan(x)를 쓰세요. (x != x라는 항등식은 NaN일 때만 참이고, isnan이 흔히 그렇게 구현되며, 그것이 없는 툴체인을 만났을 때 쓸 만한 대안입니다.)

무한대는 오버플로(exp(1000.0))에서 오고, 0이 아닌 double을 0으로 나눌 때도 옵니다. 정수의 0 나누기와 달리 이것은 정의되어 있고 죽지 않습니다.

float와 long double 변형

모든 함수에 형태가 셋 있습니다. double 판, f가 붙은 float 판, l이 붙은 long double 판입니다.

float       sqrtf(float x);
double      sqrt(double x);
long double sqrtl(long double x);

측정된 이유가 없다면 평범한 double 판을 쓰세요. double이 C가 기본으로 승격하는 타입이고, float 변형을 섞으면 보통 절약하는 것보다 변환 비용이 더 듭니다. 접미사가 중요한 유일한 자리는 double을 지원하지 않는 하드웨어에서 float만 쓰는 계산의 성능이 중요할 때입니다.

실전 예제

몇 가지를 한데 모아 봅시다. 두 점 사이의 거리와 이차방정식의 근입니다.

disc == 0.0이 아니라 fabs(disc) < 1e-12인 점을 보세요. 앞과 같은 허용 오차 규칙을, 측정된 값에서 계산된 판별식이 정확히 0에 떨어지는 일이 거의 없는 자리에 적용한 것입니다.

<math.h>는 더 큰 도구 상자 안의 헤더 하나입니다. 표준 라이브러리 페이지가 나머지를 안내하고, 여기가 아니라 <stdlib.h>에 사는 rand난수에서 다룹니다.

자주 묻는 질문

C에서 수학 함수는 어떻게 쓰나요?

파일 맨 위에 #include <math.h>를 넣고 호출합니다. sqrt(16.0), pow(2.0, 10.0), fabs(-3.5)처럼요. 인자와 반환값이 모두 double입니다. 리눅스에서는 -lm으로 수학 라이브러리도 링크해야 합니다. gcc program.c -o program -lm입니다.

컴파일할 때 'undefined reference to sqrt'가 왜 나오나요?

헤더가 컴파일러에게 선언을 줬지만, 구현은 리눅스에서 링커가 자동으로 가져오지 않는 별도 라이브러리에 있습니다. 명령의 -lm을 붙이세요. gcc program.c -o program -lm입니다. macOS와 윈도우의 MinGW에서는 수학 코드가 표준 C 라이브러리의 일부라 플래그가 필요 없습니다.

C에서 abs와 fabs의 차이는 무엇인가요?

<stdlib.h>abs()int를 받아 int를 반환합니다. <math.h>fabs()double을 받아 double을 반환합니다. abs(-3.7)을 호출하면 인자가 먼저 int로 변환되어 3이 나오고 소수 부분이 조용히 버려집니다. 부동소수점 값에는 fabs를 쓰세요.

C에서 NaN은 어떻게 확인하나요?

<math.h>isnan(x)를 쓰세요. x == NAN으로는 검사할 수 없습니다. NaN은 자기 자신을 포함해 모든 것과 다르게 비교되기 때문입니다. 사실 그 특성이 대체 검사이기도 합니다. x != x는 NaN일 때만 참입니다. 무한대에는 isinf(x), 평범한 숫자인지 보려면 isfinite(x)를 쓰세요.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기