짧은 답변:
pow(x, n)
to where n
is a natural number 의 전문화 는 종종 시간 성능에 유용 합니다 . 그러나 표준 라이브러리의 제네릭은 pow()
여전히이 목적을 위해 꽤 잘 작동하며 ( 놀랍게도! ) 표준 C 라이브러리에 가능한 한 적게 포함하여 이식 가능하고 구현하기 쉽게 만들 수 있도록하는 것이 절대적으로 중요합니다. 다른 한편으로는 C ++ 표준 라이브러리 나 STL에있는 것을 전혀 막지 못합니다. 어느 누구도 어떤 종류의 임베디드 플랫폼에서 사용할 계획이 없다고 확신합니다.
자, 긴 대답입니다.
pow(x, n)
n
자연수 를 전문화 하여 많은 경우 훨씬 더 빠르게 만들 수 있습니다 . 필자는 작성하는 거의 모든 프로그램에 대해이 함수를 직접 구현해야했습니다 (하지만 C로 많은 수학적 프로그램을 작성합니다). 특수 작업은 O(log(n))
시간 내에 수행 할 수 있지만 n
작은 경우 더 간단한 선형 버전이 더 빠를 수 있습니다. 다음은 둘 다의 구현입니다.
// Computes x^n, where n is a natural number.
double pown(double x, unsigned n)
{
double y = 1;
// n = 2*d + r. x^n = (x^2)^d * x^r.
unsigned d = n >> 1;
unsigned r = n & 1;
double x_2_d = d == 0? 1 : pown(x*x, d);
double x_r = r == 0? 1 : x;
return x_2_d*x_r;
}
// The linear implementation.
double pown_l(double x, unsigned n)
{
double y = 1;
for (unsigned i = 0; i < n; i++)
y *= x;
return y;
}
( x
의 결과가 원하는 pow(double x, unsigned n)
만큼 자주 두 배에 맞기 때문에 나는 떠났고 반환 값은 두 배가 pow(double, double)
됩니다.)
(예, pown
재귀,하지만 스택을 위반하면 최대 스택 크기 것입니다 거의 동일하기 때문에 절대적으로 불가능 log_2(n)
하고 n
정수입니다. 만약이 n
64 비트 정수입니다, 당신에게 (64)에 대해 최대 스택 크기 제공이 없음 하드웨어는 극단적있다을 메모리 제한, 하드웨어 스택이 3 ~ 8 개의 함수 호출 깊이로만 이동하는 일부 이상한 PIC를 제외하고.)
성능에 관해서는 정원 버라이어티 pow(double, double)
가 할 수있는 것에 놀랄 것 입니다. 5 년된 IBM Thinkpad x
에서 반복 횟수와 n
동일하고 10 과 동일 하게 1 억 반복을 테스트 pown_l
했습니다. 이 시나리오에서는 성공했습니다. glibc pow()
는 사용자 12.0 초, pown
7.4 초, pown_l
6.5 초만 소요되었습니다. 그리 놀라운 일이 아닙니다. 우리는 이것을 다소 기대하고있었습니다.
그런 다음 x
일정하게 유지하고 (2.5로 설정) n
0에서 19까지 1 억 번 반복했습니다. 이번에는 예상외로 glibc가 pow
이겼고 산사태가 발생했습니다! 사용자는 2.0 초 밖에 걸리지 않았습니다. 내가 pown
9.6 초 걸렸습니다, 그리고 pown_l
12.2 초 걸렸습니다. 여기 뭔 일 있었 니? 알아 내기 위해 또 다른 테스트를했습니다.
나는 위와 같은 일을 x
백만 과 동일하게했다. 이번에 pown
는 9.6 초로 승리했다. pown_l
12.2 초, glibc pow는 16.3 초였습니다. 자, 분명합니다! glibc 는 낮을 pow
때 세 가지보다 더 잘 수행 x
되지만 x
높을 때 가장 좋지 않습니다. x
높을 때 pown_l
가장 잘 수행 n
되고 높을 pown
때 가장 잘 수행됩니다 x
.
여기에 세 가지 다른 알고리즘이 있으며, 각각은 적절한 상황에서 다른 알고리즘보다 더 잘 수행 할 수 있습니다. 따라서 궁극적으로 사용할 방법은을 사용하려는 계획에 따라 다르지만 pow
올바른 버전을 사용하는 것이 가치가 있으며 모든 버전을 갖는 것이 좋습니다. 실제로 다음과 같은 함수를 사용하여 알고리즘 선택을 자동화 할 수도 있습니다.
double pown_auto(double x, unsigned n, double x_expected, unsigned n_expected) {
if (x_expected < x_threshold)
return pow(x, n);
if (n_expected < n_threshold)
return pown_l(x, n);
return pown(x, n);
}
만큼 x_expected
와 n_expected
있는 가능성이 다른주의 사항과 함께, 컴파일시에 결정 상수, 그의 염은 자동으로 전체를 제거하는 것이다 최적화 컴파일러 가치 pown_auto
함수 호출을하고 3 개 개의 알고리즘의 적절한 선택으로 대체. (이제 실제로 이것을 사용 하려는 경우 위에서 작성한 내용을 정확히 컴파일 하지 않았기 때문에 약간 놀아야 할 것입니다.;))
반면에 glibc pow
는 작동 하며 glibc는 이미 충분히 큽니다. C 표준은 다양한 임베디드 장치를 포함하여 이식 가능해야 합니다 (사실 모든 임베디드 개발자는 일반적으로 glibc가 이미 너무 크다는 데 동의합니다). 모든 간단한 수학 함수에 대해 모든 것을 포함해야하는 경우 이식 할 수 없습니다. 사용할 수 있는 대체 알고리즘 . 그래서 그것이 C 표준에없는 이유입니다.
footnote : 시간 성능 테스트에서 나는 -s -O2
내 시스템 (archlinux)에서 glibc를 컴파일하는 데 사용되었을 가능성이있는 것보다 나쁘지는 않더라도 비교할 수있는 비교적 관대 한 최적화 플래그 ( )를 내 함수 에 제공 했으므로 결과는 아마도 공정한. 더 엄격한 테스트를 위해, 나 자신의 glibc 컴파일해야 할 것 나는 reeeally 그렇게 기분이 안. 저는 Gentoo를 사용했기 때문에 작업이 자동화 된 경우에도 시간이 얼마나 걸리는지 기억합니다 . 결과는 나에게 충분히 결정적 (또는 다소 결정적이지 않음)입니다. 물론이 작업을 직접 수행 할 수 있습니다.
보너스 라운드 :의 전문화 pow(x, n)
모든 정수로는 쓸모 정확한 정수 출력이 일어날 않는, 필요한 경우. p ^ N 요소가있는 N 차원 배열에 대한 메모리 할당을 고려하십시오. p ^ N을 하나씩 꺼도 무작위로 발생하는 segfault가 발생할 수 있습니다.