표준 C ++ 라이브러리에서`int pow (int base, int exponent)`가 아닌 이유는 무엇입니까?


116

나는 그것을 찾을 수 없을 것 같다. C ++ pow함수가 floats 및 doubles를 제외하고 "power"함수를 구현하지 않는 이유가 있습니까?

구현이 사소하다는 것을 알고 있으며 표준 라이브러리에 있어야하는 작업을하고있는 것처럼 느껴집니다. 강력한 power 함수 (즉, 일관되고 명시적인 방식으로 오버플로 처리)는 작성하는 것이 재미 있지 않습니다.


4
이것은 좋은 질문이며 대답이별로 의미가 없다고 생각합니다. 음의 지수가 작동하지 않습니까? 부호없는 정수를 지수로 사용합니다. 대부분의 입력으로 인해 오버플로가 발생합니까? exp와 double pow도 마찬가지입니다. 아무도 불평하지 않습니다. 그렇다면이 기능이 표준이 아닌 이유는 무엇입니까?
static_rtti

2
@static_rtti : "exp와 double pow도 마찬가지입니다."는 완전히 거짓입니다. 내 대답에 대해 자세히 설명하겠습니다.
Stephen Canon

11
표준 C ++ 라이브러리는 double pow(int base, int exponent)C ++ 11 이후 (§26.8 [c.math] / 11 bullet point 2)
Cubbi

'구현은 사소하다'와 '작성하는 것이 재미 없다'사이에서 마음을 정해야합니다.
론의 후작

답변:


66

현재 C++11, 전원 함수 (및 기타) 제품군에 특수 사례가 추가되었습니다. C++11 [c.math] /11모든 float/double/long double오버로드를 나열한 후 상태 (내 강조 및 의역) :

또한 매개 변수에 해당하는 인수 가 유형 또는 정수 유형 을 갖는 경우 매개 변수에 해당하는 모든 인수 가 효과적으로 캐스트 되도록 보장하기에 충분한 추가 오버로드가 있어야합니다 .doubledoubledoubledouble

따라서 기본적으로 정수 매개 변수는 연산을 수행하기 위해 두 배로 업그레이드됩니다.


이전에는 C++11(질문이 요청되었을 때) 정수 오버로드가 없었습니다.

나는 어느 쪽도 밀접의 제작자와 연관되지 않았기 때문에 CC++(내가 비록 그들의 창조의 일 입니다 오히려 이전)이나 표준을 만든 ANSI / ISO위원회의 한 부분이 반드시 나의 부분에 대한 의견입니다. 나는 그것이 정보에 입각 한 의견 이라고 생각하고 싶지만, 아내가 당신에게 말할 것입니다 (자주 그리고 많은 격려가 필요하지 않음), 나는 전에 틀 렸습니다 :-)

그 가치에 대한 가정은 다음과 같습니다.

나는 의심 원래 사전 ANSI가 이유 있다고 C는 완전히 불필요하기 때문에이 기능을하지 않았다입니다. 첫째, 정수 거듭 제곱을 수행하는 완벽하게 좋은 방법이 이미있었습니다 (복수를 사용하고 간단히 정수로 다시 변환하여 변환하기 전에 정수 오버플로 및 언더 플로를 확인).

둘째, 당신이 기억해야 할 또 다른 점은의 원래 의도가 있다는 것이다 CA와이었다 시스템 언어 프로그래밍, 그리고 부동 소수점하는 것은 전혀 그 분야에서 바람직 있는지 의문이다.

초기 사용 사례 중 하나가 UNIX를 코딩하는 것이었기 때문에 부동 소수점은 거의 쓸모가 없었을 것입니다. C가 기반을 둔 BCPL은 또한 거듭 제곱을 사용하지 않았습니다 (메모리에서 부동 소수점이 전혀 없음).

제쳐두고, 적분 전력 연산자는 아마도 라이브러리 호출이 아니라 이항 연산자 일 것입니다. 당신은 두 개의 정수를 추가하지 마십시오 x = add (y, z)하지만과 x = y + z의 부분 - 언어 적절한 보다는 도서관.

셋째, 적분 력의 구현이 비교적 사소하기 때문에 언어 개발자가 더 유용한 정보를 제공하는 데 시간을 더 잘 사용할 것이라고 거의 확신합니다 (기회 비용에 대한 아래 설명 참조).

그것은 또한 원본과 관련이 C++있습니다. 원래 구현은 사실상 C코드 를 생성하는 번역기 였기 때문에 C. 원래 의도는 C-with-classes-plus-a-little-bit-of-extra-math-stuff가 아니라 C-with-classes였습니다.

이전 C++11에 표준에 추가 된 적이없는 이유 는 표준 설정 기관에 따라야 할 특정 지침이 있다는 것을 기억해야합니다. 예를 들어 ANSI C는 새 언어를 만드는 것이 아니라 기존 관행을 체계화하는 작업을 특별히 맡았습니다 . 그렇지 않으면 그들은 미쳐서 우리에게 Ada를 줄 수 있습니다 :-)

이 표준의 이후 반복에는 특정 지침이 있으며 근거 문서에서 찾을 수 있습니다 (위원회가 언어 자체에 대한 근거가 아닌 특정 결정을 내린 이유에 대한 근거).

예를 들어, C99근거 문서는 C89추가 할 수있는 내용을 제한하는 두 가지 기본 원칙을 구체적으로 전달합니다 .

  • 언어를 작고 단순하게 유지하십시오.
  • 작업을 수행하는 한 가지 방법 만 제공하십시오.

가이드 라인 ( 특정한 것이 아닐 수도 있음 )은 개별 작업 그룹에 대해 정해져 있으므로 C++위원회 (및 기타 모든 ISO 그룹)도 제한합니다.

또한 표준 설정 기관은 자신이 내리는 모든 결정에 기회 비용 (결정을 내리기 위해 포기해야하는 것을 의미하는 경제 용어)이 있음을 인식합니다. 예를 들어, $ 10,000 uber-gaming 기계를 구입하는 기회 비용은 약 6 개월 동안 다른 절반과의 따뜻한 관계 (또는 모든 관계)입니다.

Eric Gunnerson 은 왜 항상 Microsoft 제품에 추가되지 않는지에 대한 -100 점 설명 으로 이를 잘 설명합니다. 기본적으로 기능은 구멍에서 100 점을 시작하므로 고려할 가치가 상당히 추가되어야합니다.

다시 말해서, 통합 전력 연산자 (솔직히 말하면 어떤 반 괜찮은 코더라도 10 분 안에 작동 할 수 있음) 또는 멀티 스레딩을 표준에 추가 하시겠습니까? 나 자신을 위해 나는 후자를 선호하고 UNIX와 Windows에서 다른 구현에 대해 고민 할 필요가 없습니다.

또한 표준 라이브러리 (해시, btrees, 빨강-검정 트리, 사전, 임의지도 등)에 대한 수천 개의 컬렉션을보고 싶습니다만, 근거에 따르면 다음과 같습니다.

표준은 구현 자와 프로그래머 간의 조약입니다.

그리고 표준기구의 구현 자 수는 프로그래머 (또는 적어도 기회 비용을 이해하지 못하는 프로그래머) 수보다 훨씬 큽니다. 모든 재료가 추가 된 경우, 다음의 표준이 C++될 것이다 C++215x아마 완전히 삼백년 그 이후 컴파일러 개발자에 의해 구현 될 것입니다.

어쨌든 그것은 그 문제에 대한 나의 (다소 방대한) 생각입니다. 질보다는 양을 기준으로 투표 만했다면 곧 다른 사람들을 물 밖으로 날려 버릴 것입니다. 듣기 주셔서 감사합니다 :-)


2
FWIW, 저는 C ++가 "작업을 수행하는 한 가지 방법 만 제공"을 제약 조건으로 따르지 않는다고 생각합니다. 예를 들어 to_string람다와 람다는 둘 다 이미 할 수있는 일에 편리하기 때문입니다. 나는 "오직 한 가지 작업을 수행하는 방법"을 매우 느슨하게 해석 하여 두 가지를 모두 허용하고 동시에 "아하! 안돼! 그것은 정확하게 동등하지만 더 긴 바람의 대안과는 미묘하게 다른 작업입니다! ". 확실히 람다에 해당합니다.
Steve Jessop 2012 년

@Steve, 예, 그것은 내 부분에서 나쁘게 표현되었습니다. 모든위원회가 동일한 지침을 따르는 것보다 각위원회에 대한 지침이 있다고 말하는 것이 더 정확합니다. clarifyl에 조정 대답
paxdiablo

2
단 한 가지 (몇 개 중) : "모든 코드 원숭이는 10 분 안에 휩쓸 릴 수 있습니다." 물론, 100 명의 코드 원숭이 (멋진 모욕적 인 용어, BTW)가 매년 그렇게한다면 (아마도 낮은 추정치) 1000 분을 낭비하게됩니다. 매우 효율적이라고 생각하지 않습니까?
Jürgen A. Erhard 2013 년

1
@ Jürgen, 모욕적 인 것이 아닙니다 (실제로 레이블을 특정 사용자에게 지정 pow하지 않았기 때문에). 실제로 많은 기술이 필요하지 않은 표시 일뿐 입니다. 확실히 나는 표준 이 많은 기술 필요한 것을 제공 하고 노력을 중복해야 할 경우 훨씬 더 많은 시간을 낭비하게 만들고 싶습니다 .
paxdiablo

2
@ eharo2, 현재 텍스트의 "half decent coder"를 "code monkey"로 바꾸면됩니다. 저도 모욕적이라고 생각하지 않았지만 조심하는 것이 최선이라고 생각했고 솔직히 말해서 현재의 표현은 같은 생각을 전달합니다.
paxdiablo

41

고정 너비 정수 유형의 경우 거의 모든 가능한 입력 쌍이 유형을 오버플로합니다. 대부분의 가능한 입력에 대해 유용한 결과를 제공하지 않는 함수를 표준화하는 용도는 무엇입니까?

함수를 유용하게 만들려면 큰 정수 유형이 필요하며 대부분의 큰 정수 라이브러리는 함수를 제공합니다.


편집 : 질문에 대한 주석에서 static_rtti는 "대부분의 입력으로 인해 오버플로가 발생합니까? exp 및 double pow도 마찬가지입니다. 불평하는 사람이 보이지 않습니다."라고 씁니다. 이것은 올바르지 않습니다.

exp(실제로 내 사례를 더 강하게 만들지 만) 요점을 벗어난 것이므로 옆에두고 double pow(double x, double y). (x, y) 쌍의 어떤 부분에 대해이 함수가 유용한 작업을 수행합니까 (예 : 단순히 오버플로 또는 언더 플로가 아님)?

pow제 요점을 증명하기에 충분할 것이기 때문입니다. 만약 x가 양수이고 | y |라면 , 입력 쌍의 작은 부분에만 집중할 것입니다. <= 1이면 pow오버플로 또는 언더 플로하지 않습니다. 이것은 모든 부동 소수점 쌍의 거의 1/4로 구성됩니다 (정확히 NaN이 아닌 부동 소수점 숫자의 절반이 양수이고 NaN이 아닌 부동 소수점 숫자의 절반 미만이 크기가 1보다 작음). 분명히 유용한 결과 를 생성 하는 다른 입력 쌍이 많이pow 있지만 모든 입력의 1/4 이상임을 확인했습니다.

이제 고정 너비 (즉, bignum이 아닌) 정수 거듭 제곱 함수를 살펴 보겠습니다. 어떤 부분 입력에 대해 단순히 넘치지 않습니까? 의미있는 입력 쌍의 수를 최대화하려면 밑이 부호가 있고 지수는 부호가 없어야합니다. 밑과 지수가 모두 n비트 폭 이라고 가정합니다 . 의미있는 입력 부분에 대한 경계를 쉽게 얻을 수 있습니다.

  • 지수가 0 또는 1이면 모든 밑이 의미가 있습니다.
  • 지수가 2 이상이면 2 ^ (n / 2)보다 큰 기수가 의미있는 결과를 생성하지 않습니다.

따라서 2 ^ (2n) 입력 쌍 중 2 ^ (n + 1) + 2 ^ (3n / 2)보다 작 으면 의미있는 결과가 생성됩니다. 가장 일반적인 사용법 인 32 비트 정수를 살펴보면 이는 입력 쌍의 1 %의 1/1000 정도가 단순히 오버플로되지 않음을 의미합니다.


8
어쨌든이 모든 것은 문제입니다. 함수가 일부 또는 많은 입력에 유효하지 않다고해서 유용성이 떨어지지는 않습니다.
static_rtti 2011-07-06

2
@static_rtti : pow(x,y)| y | 인 경우 x에 대해 0으로 언더 플로하지 않습니다 . <= 1. 언더 플로가 발생 하는 매우 좁은 입력 대역 (큰 x, y 거의 -1)이 있지만 그 결과는 해당 범위에서 여전히 의미가 있습니다.
Stephen Canon

2
더 많은 생각을하면서 나는 언더 플로에 동의합니다. 나는 여전히 이것이 질문과 관련이 없다고 생각합니다.
static_rtti 2011-07-06

7
@ybungalobill : 왜 그걸 이유로 선택하셨습니까? 개인적으로 저는 많은 문제와 프로그래머에게 유용하고 대부분의 프로그래머가 작성하는 순진한 구현보다 빠른 하웨어 최적화 버전을 만들 수있는 가능성을 선호합니다. 당신의 기준은 완전히 임의적이며 솔직히 말해서 무의미합니다.
static_rtti 2011-07-07

5
@StephenCanon : 밝은면에서 당신의 주장은 명백히 정확하고 최적의 정수 구현 pow이 단순히 작은 조회 테이블 이라는 것을 보여줍니다 . :-)
R .. GitHub STOP HELPING ICE

11

어쨌든 int의 모든 정수 거듭 제곱을 나타낼 수있는 방법이 없기 때문에 :

>>> print 2**-4
0.0625

3
유한 크기의 숫자 유형의 경우 오버플로로 인해 해당 유형 내에서 해당 유형의 모든 거듭 제곱을 나타낼 방법이 없습니다. 그러나 부정적인 힘에 대한 당신의 주장은 더 타당합니다.
Chris Lutz

1
음의 지수는 표준 구현이 처리 할 수있는 것으로 봅니다. unsigned int를 지수로 취하거나 음의 지수가 입력으로 제공되고 int가 예상 출력 일 때 0을 반환합니다.
Dan O

3
또는 별도 int pow(int base, unsigned int exponent)float pow(int base, int exponent)
Ponkadoodle 2010 년

4
음의 정수를 전달하기 위해 정의되지 않은 동작으로 선언 할 수 있습니다.
Johannes Schaub-litb

2
모든 현대적 구현에서 그 밖의 모든 것은 int pow(int base, unsigned char exponent)어쨌든 다소 쓸모가 없습니다. 밑수는 0 또는 1이고 지수는 중요하지 않습니다. -1입니다.이 경우 지수의 마지막 비트 만 문제가되고, base >1 || base< -1이 경우 exponent<256오버플로의 패널티가 적용됩니다.
MSalters

9

실제로 흥미로운 질문입니다. 토론에서 찾지 못한 한 가지 인수는 인수에 대한 명백한 반환 값이 없다는 것입니다. 최면 int pow_int(int, int)기능이 실패 할 수있는 방법을 세어 보겠습니다 .

  1. 과다
  2. 결과가 정의되지 않음 pow_int(0,0)
  3. 결과를 표현할 수 없습니다. pow_int(2,-1)

이 기능에는 최소 2 개의 실패 모드가 있습니다. 정수는 이러한 값을 나타낼 수 없습니다. 이러한 경우 함수의 동작은 표준에 의해 정의되어야하며 프로그래머는 함수가 이러한 경우를 정확히 처리하는 방법을 알고 있어야합니다.

전반적으로 기능을 제외하는 것이 유일한 현명한 옵션처럼 보입니다. 프로그래머는 대신 사용 가능한 모든 오류보고와 함께 부동 소수점 버전을 사용할 수 있습니다.


그러나 처음 두 경우 pow는 플로트 사이 에도 적용되지 않습니까? 두 개의 큰 수레를 가지고 하나를 다른 하나의 힘으로 올리면 오버플로가 발생합니다. 그리고 pow(0.0, 0.0)두 번째 포인트와 동일한 문제가 발생합니다. 세 번째 점은 정수와 부동 소수점에 대한 멱 함수를 구현하는 유일한 실제 차이점입니다.
numbermaniac

7

짧은 답변:

pow(x, n)to where nis 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정수입니다. 만약이 n64 비트 정수입니다, 당신에게 (64)에 대해 최대 스택 크기 제공이 없음 하드웨어는 극단적있다을 메모리 제한, 하드웨어 스택이 3 ~ 8 개의 함수 호출 깊이로만 이동하는 일부 이상한 PIC를 제외하고.)

성능에 관해서는 정원 버라이어티 pow(double, double)가 할 수있는 것에 놀랄 것 입니다. 5 년된 IBM Thinkpad x에서 반복 횟수와 n동일하고 10 과 동일 하게 1 억 반복을 테스트 pown_l했습니다. 이 시나리오에서는 성공했습니다. glibc pow()는 사용자 12.0 초, pown7.4 초, pown_l6.5 초만 소요되었습니다. 그리 놀라운 일이 아닙니다. 우리는 이것을 다소 기대하고있었습니다.

그런 다음 x일정하게 유지하고 (2.5로 설정) n0에서 19까지 1 억 번 반복했습니다. 이번에는 예상외로 glibc가 pow이겼고 산사태가 발생했습니다! 사용자는 2.0 초 밖에 걸리지 않았습니다. 내가 pown9.6 초 걸렸습니다, 그리고 pown_l12.2 초 걸렸습니다. 여기 뭔 일 있었 니? 알아 내기 위해 또 다른 테스트를했습니다.

나는 위와 같은 일을 x백만 과 동일하게했다. 이번에 pown는 9.6 초로 승리했다. pown_l12.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_expectedn_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가 발생할 수 있습니다.


재귀를 제거하면 스택 할당에 필요한 시간을 절약 할 수있을 것 같습니다. 그리고 예, 우리는 포로가 모든 것을 늦추고 우리 자신의 포로를 구현해야하는 상황이있었습니다.
Sambatyon

"그렇게 극심한 메모리 제한이있는 사람은 없습니다"는 거짓입니다. PIC는 종종 최대 3 개 (예 : PIC10F200)에서 8 개 (예 : 16F722A) 호출에 대해 제한된 호출 스택을 갖습니다 (PIC는 함수 호출에 하드웨어 스택 사용).
12431234123412341234123

아, 잔인하네요. 좋습니다. 따라서 해당 PIC에서는 작동하지 않습니다.
enigmaticPhysicist

질문이 묻는 것과 같이 정수 기반과 전력의 경우 컴파일러 (gcc 및 clang)는 반복 (재귀 대신) 구현에서 분기없는 루프를 쉽게 생성합니다. 이것은의 각 비트에서 분기 오류 예측을 방지 n합니다. godbolt.org/z/L9Kb98 . gcc 및 clang은 재귀 정의를 간단한 루프로 최적화하지 못하고 실제로 n. ( pown_iter(double,unsigned)여전히 분기되지만 분기없는 SSE2 또는 SSE4.1 구현은 x86 asm 또는 C intrinsics에서 가능해야합니다.하지만 재귀보다 낫습니다.)
Peter Cordes

젠장, 이제 확실하게 루프 기반 버전으로 벤치 마크를 다시해야합니다. 나는 그것에 대해 생각합니다.
enigmaticPhysicist

6

C ++에 추가 오버로드가없는 한 가지 이유는 C와 호환되기 때문입니다.

C ++ 98에는와 같은 함수가 double pow(double, int)있지만 C99에 포함되지 않았다는 인수와 함께 C ++ 11에서는 이러한 함수 가 제거되었습니다.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3286.html#550

약간 더 정확한 결과를 얻는다는 것은 또한 약간 다른 결과를 얻는 것을 의미 합니다.


3

세상은 끊임없이 진화하고 프로그래밍 언어도 마찬가지입니다. 그만큼C 십진수 TR¹ 네 번째 부분은에 더 많은 함수를 추가합니다 <math.h>. 이러한 기능의 두 가지 제품군이이 질문에 관심이있을 수 있습니다.

  • 그만큼 pown기능, 즉 부동 소수점 숫자 및 소요 intmax_t지수.
  • powr두 개의 부동 소수점 숫자 ( xy)를 사용 x하여 거듭 제곱 하는 함수y 수식exp(y*log(x)) 입니다.

표준 사람들은 결국 이러한 기능을 표준 라이브러리에 통합하기에 충분히 유용하다고 생각한 것 같습니다. 그러나이 기능은 ISO / IEC / IEEE 60559 : 2011 표준에서 2 진수 및 10 진수 부동 소수점 숫자에 권장된다는 것이 합리적입니다 . C89 당시 "표준"이 무엇인지 확실히 말할 수는 없지만<math.h> 은 아마도 ISO / IEC / IEEE 60559 표준 의 향후 발전에 크게 영향을받을 것입니다 .

소수점 TR의 네 번째 부분은 C2x (다음 주요 C 개정판)에 포함되지 않으며 나중에 선택적 기능으로 포함될 것입니다. 향후 C ++ 개정판에 TR의이 부분을 포함시키려는 의도가 없었습니다.


¹ 여기 에서 작업 진행중인 문서를 찾을 수 있습니다 .


사용되는 임의의 그럴듯한 구현물 pown보다 큰 지수와 LONG_MAX적 사용에서 서로 다른 값을 산출한다은 LONG_MAX, 어디서 미만의 값 LONG_MIN에서 다른 값을 산출한다 LONG_MIN? intmax_t지수 를 사용하면 어떤 이점이 있는지 궁금 합니다.
supercat 2015-09-16

@supercat 모르겠어요, 죄송합니다.
Morwenn 2015 년

표준을 살펴보면, 정의 된 경우 "pown"의 올바른 반올림 버전이 될 선택적 "crpown"함수도 정의하는 것으로 보입니다. 그렇지 않으면 표준은 필요한 정확도를 지정하지 않습니다. 빠르고 적당히 정확한 "pown"을 구현하는 것은 쉽지만 모든 경우에 올바른 반올림을 보장하는 것은 훨씬 더 비싸기 쉽습니다.
supercat

2

아마도 프로세서의 ALU가 정수에 대해 그러한 함수를 구현하지 않았기 때문일 수 있지만 그러한 FPU 명령이 있습니다 (Stephen이 지적했듯이 실제로 쌍입니다). 따라서 실제로는 정수 연산을 사용하여 구현하는 것보다 double로 캐스팅하고, double로 pow를 호출 한 다음 오버플로를 테스트하고 다시 캐스팅하는 것이 더 빠릅니다.

(한 가지로, 로그는 곱셈에 대한 힘을 줄이지 만 정수의 로그는 대부분의 입력에 대해 많은 정확도를 잃습니다)

Stephen은 현대 프로세서에서는 이것이 더 이상 사실이 아니지만 수학 함수가 선택되었을 때 (C ++는 방금 C 함수를 사용함) C 표준이 이제 20 년이 된 것입니다.


5
에 대한 FPU 명령이있는 현재 아키텍처를 모르겠습니다 pow. x86에는 함수 의 첫 부분으로 사용할 수 있는 y log2 x명령어 ( fyl2x)가 pow있지만 이렇게 pow작성된 함수는 현재 하드웨어에서 실행하는 데 수백 번의 사이클이 걸립니다. 잘 작성된 정수 지수화 루틴은 몇 배 더 빠릅니다.
Stephen Canon

"수백"이 정확한지 모르겠습니다. 대부분의 최신 CPU에서 fyl2x 다음 f2xm1에 대해 약 150 사이클 인 것으로 보이며 다른 명령과 함께 파이프 라인됩니다. 그러나 IMUL이 부동 소수점 명령어보다 훨씬 더 빨라 졌기 때문에 잘 조정 된 정수 구현이 훨씬 빠르다는 것이 맞습니다. C 표준이 작성되었을 때 IMUL은 상당히 비 쌌고 루프에서 사용하는 것은 FPU를 사용하는 것보다 더 오래 걸렸습니다.
Ben Voigt

2
수정에 비추어 내 투표를 변경했습니다. 그래도 (a) 1999 년에 C 표준이 대대적 인 개정 (수학 라이브러리의 대규모 확장 포함)을 거쳤으며 (b) C 표준이 특정 프로세서 아키텍처에 작성되지 않았다는 점을 명심하십시오. 또는 x86에서 FPU 명령이 없다는 것은 C위원회가 표준화하기 위해 선택한 기능과 본질적으로 관련이 없습니다.
Stephen Canon

사실 어떤 아키텍처에도 묶여 있지는 않지만 정수 곱셈과 비교하여 조회 테이블 보간 (일반적으로 부동 소수점 구현에 사용됨)의 상대적 비용은 내가 추측하는 모든 아키텍처에서 거의 동일하게 변경되었습니다.
Ben Voigt

1

다음은 정수를 포함한 모든 숫자 유형에 대해 작동하는 pow () 의 정말 간단한 O (log (n)) 구현입니다 .

template<typename T>
static constexpr inline T pown(T x, unsigned p) {
    T result = 1;

    while (p) {
        if (p & 0x1) {
            result *= x;
        }
        x *= x;
        p >>= 1;
    }

    return result;
}

재귀를 사용하지 않기 때문에 enigmaticPhysicist의 O (log (n)) 구현보다 낫습니다.

또한 거의 항상 선형 구현보다 빠릅니다 (p> ~ 3 인 경우).

  • 추가 메모리가 필요하지 않습니다.
  • 루프 당 최대 1.5 배 더 많은 작업을 수행합니다.
  • 루프 당 ~ 1.25 배 더 많은 메모리 업데이트 만 수행합니다.

-2

사실 그렇습니다.

C ++ 11부터 pow(int, int)--- 의 템플릿 구현이 있으며 더 일반적인 경우는 http://en.cppreference.com/w/cpp/numeric/math/pow의 (7)을 참조하십시오 .


편집 : 순수 주의자들은 실제로 "승진 된"타이핑이 사용되기 때문에 이것이 정확하지 않다고 주장 할 수 있습니다. 어떤 식 으로든 매개 변수 int에 대한 올바른 결과 또는 오류를 얻습니다 int.


2
이것은 올바르지 않습니다. (7) 오버로드는 pow ( Arithmetic1 base, Arithmetic2 exp )캐스트 double되거나 long double설명을 읽은 경우입니다. "7) 1-3에서 다루지 않는 산술 유형의 모든 인수 조합에 대한 오버로드 집합 또는 함수 템플릿. 정수 유형이 있으면 double로 캐스트됩니다. 인수가 long double이면 Promoted 반환 유형도 long double이고 그렇지 않으면 반환 유형은 항상 double입니다. "
phuclv

여기서 잘못된 것은 무엇입니까? 나는 단지 오늘날 (C ++ 11 이후) 템플릿 화 된 pow ( , )가 표준 라이브러리에
있다고 말했을뿐입니다

5
아니에요. Templeates는 이러한 유형을 double 또는 long double로 홍보합니다. 그래서 그것은 아래 복식에서 작동합니다.
Trismegistos

1
@Trismegistos 여전히 int 매개 변수를 허용합니다. 이 템플릿이 없으면 int 매개 변수를 전달하면 int의 비트를 부동 소수점으로 해석하여 임의의 예기치 않은 결과가 발생합니다. 혼합 입력 값에서도 마찬가지입니다. 예 pow(1.5f, 3)= 1072693280하지만 pow(1.5f, float(3))=3.375
Mark Jeronimus

2
OP는을 요청 int pow(int, int)했지만 C ++ 11은 double pow(int, int). @phuclv의 설명을 참조하십시오.
xuhdev

-4

아주 간단한 이유 :

5^-2 = 1/25

STL 라이브러리의 모든 것은 상상할 수있는 가장 정확하고 강력한 것을 기반으로합니다. 물론 int는 (1/25에서) 0으로 반환되지만 이것은 부정확 한 대답입니다.

동의합니다. 어떤 경우에는 이상합니다.


3
서명되지 않은 두 번째 인수를 요구하는 것은 분명히 필요합니다. 음이 아닌 정수 거듭 제곱 만 필요한 많은 응용 프로그램이 있습니다.
enigmaticPhysicist
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.