Math.Pow ()는 .NET Framework에서 어떻게 구현됩니까?


432

나는 b 를 계산하기위한 효율적인 접근법을 찾고있었습니다 (예 : a = 2b = 50). 작업을 시작하기 위해 Math.Pow()기능 구현을 살펴보기로 결정했습니다 . 그러나 .NET Reflector 에서 내가 찾은 것은 다음과 같습니다.

[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern double Pow(double x, double y);

Math.Pow()함수를 호출 할 때 무슨 일이 벌어지고 있는지 확인할 수있는 리소스는 무엇 입니까?


15
참고로, 수정 자 InternalCall와 혼동하는 경우 extern( 충돌하는 것처럼 보임) 이 문제 에 대해 게시 한 질문 (및 결과 답변) 을 참조하십시오 .
CraigTP

6
A에 대한 2^x동작한다면 x결과가 시프트 연산 인 정수이다. 따라서의 가수 2와 지수를 사용하여 결과를 구성 할 수 있습니다 x.
ja72

@SurajJain 귀하의 의견은 실제로 별도로 게시 해야하는 질문입니다.
ja72

@SurajJain 동의합니다. 나는 사회자가 아니므로 여기서 할 수 없습니다. 아마 downvote의 질문에 질문 할 수 meta.stackoverflow.com
ja72

답변:


854

MethodImplOptions.InternalCall

즉, 메소드는 실제로 C ++로 작성된 CLR로 구현됩니다. JIT (Just-In-Time) 컴파일러는 내부적으로 구현 된 메소드가있는 테이블을 참조하고 C ++ 함수에 대한 호출을 직접 컴파일합니다.

코드를 살펴 보려면 CLR의 소스 코드가 필요합니다. SSCLI20 배포판 에서 얻을 수 있습니다 . .NET 2.0 시간대를 중심으로 작성되었으며, 저수준 구현을 찾았습니다.Math.Pow() 은 CLR의 이후 버전에서 여전히 정확히 정확 .

조회 테이블은 clr / src / vm / ecall.cpp에 있습니다. 관련 섹션은 Math.Pow()다음과 같습니다.

FCFuncStart(gMathFuncs)
    FCIntrinsic("Sin", COMDouble::Sin, CORINFO_INTRINSIC_Sin)
    FCIntrinsic("Cos", COMDouble::Cos, CORINFO_INTRINSIC_Cos)
    FCIntrinsic("Sqrt", COMDouble::Sqrt, CORINFO_INTRINSIC_Sqrt)
    FCIntrinsic("Round", COMDouble::Round, CORINFO_INTRINSIC_Round)
    FCIntrinsicSig("Abs", &gsig_SM_Flt_RetFlt, COMDouble::AbsFlt, CORINFO_INTRINSIC_Abs)
    FCIntrinsicSig("Abs", &gsig_SM_Dbl_RetDbl, COMDouble::AbsDbl, CORINFO_INTRINSIC_Abs)
    FCFuncElement("Exp", COMDouble::Exp)
    FCFuncElement("Pow", COMDouble::Pow)
    // etc..
FCFuncEnd()

"COMDouble"을 검색하면 clr / src / classlibnative / float / comfloat.cpp로 이동합니다. 코드를 아끼지 않고 직접 살펴보십시오. 기본적으로 코너 케이스를 확인한 다음 CRT 버전을 호출합니다.pow() .

흥미로운 다른 구현 세부 사항은 테이블의 FCIntrinsic 매크로입니다. 그것은 지터가 기능을 내장 함수로 구현할 수 있다는 힌트입니다. 즉, 함수 호출을 부동 소수점 기계 코드 명령어로 대체하십시오. 에 해당하지 않는 경우 Pow()에는 FPU 명령이 없습니다. 그러나 다른 간단한 작업에는 확실히 적용됩니다. 주목할 점은 이것이 C #의 부동 소수점 수학을 C ++의 동일한 코드보다 훨씬 빠르게 만들 수 있다는 것입니다. 을 이유를 .

그런데 CRT의 소스 코드는 전체 버전의 Visual Studio vc / crt / src 디렉토리가있는 경우에도 사용할 수 있습니다. 당신이 벽에 충돌거야 pow()하지만를, 마이크로 소프트는 인텔에서 해당 코드를 구입했습니다. 인텔 엔지니어보다 더 나은 일을하는 것은 쉽지 않습니다. 고등학교 책의 정체성은 내가 시도했을 때 두 배 빠르지 만 :

public static double FasterPow(double x, double y) {
    return Math.Exp(y * Math.Log(x));
}

그러나 3 개의 부동 소수점 연산에서 오류가 누적되고 Pow ()가 가진 이상한 도메인 문제를 처리하지 않기 때문에 진정한 대안은 아닙니다. 0 ^ 0과 같이 -Infinity는 모든 힘을 올렸습니다.


437
좋은 대답은, StackOverflow는 '왜 그걸 알고 싶니?'대신에 이런 종류의 것들이 더 필요하다는 것입니다. 그것은 너무 자주 발생합니다.
Tom W

16
@Blue-Intel 엔지니어를 놀리는 데는 부족합니다. 고등학교 책에 부정적인 통합의 힘을 불어 넣는 데 문제가 있습니다. Pow (x, -2)는 완벽하게 계산 가능하며 Pow (x, -2.1)은 정의되어 있지 않습니다. 도메인 문제는 다루기 힘든 암캐입니다.
Hans Passant

12
@ BlueRaja-DannyPflughoeft : 부동 소수점 연산이 올바른 반올림 값에 최대한 가깝도록하기 위해 많은 노력이 필요합니다. pow초월적인 기능이기 때문에 정확하게 구현하기가 어렵습니다 ( Table-Maker의 딜레마 참조 ). 적분 전력으로 훨씬 쉽습니다.
porges

9
@Hans Passant : Pow (x, -2.1)이 정의되지 않은 이유는 무엇입니까? 수학적으로 pow는 모든 x와 y에 대해 모든 곳에서 정의됩니다. 음수 x와 정수가 아닌 y에 대해 복소수를 얻는 경향이 있습니다.
Jules

8
@Jules pow (0, 0)이 정의되지 않았습니다.
엠 보스

110

Hans Passant의 대답 은 훌륭하지만 b정수이면 a^b이진 분해로 매우 효율적으로 계산할 수 있다고 덧붙이고 싶습니다 . 다음은 Henry Warren의 Hacker 's Delight 에서 수정 된 버전입니다 .

public static int iexp(int a, uint b) {
    int y = 1;

    while(true) {
        if ((b & 1) != 0) y = a*y;
        b = b >> 1;
        if (b == 0) return y;
        a *= a;
    }    
}

그는이 연산이 모든 b <15에 대해 최적 (최소한의 산술 또는 논리 연산을 수행함)이라고 지적합니다. 또한 a^b광범위한 이외의 다른 b에 대해 계산할 최적의 요인 시퀀스를 찾는 일반적인 문제에 대한 알려진 해결책은 없습니다. 검색. NP-Hard 문제입니다. 따라서 기본적으로 이진 분해는 얻을 수있는만큼 좋습니다.


11
이 알고리즘 ( square and multiply ) a은 부동 소수점 숫자 인 경우에도 적용됩니다 .
코드 InChaos

14
실제로는 기본 제곱 및 곱셈보다 약간 더 잘 수행 할 수 있습니다. 예를 들어 작은 지수에 대한 룩업 테이블을 준비하여 여러 번 제곱 한 다음 곱하기 만하거나 고정 지수에 대해 최적화 된 제곱 추가 체인을 만들 수 있습니다. 이러한 종류의 문제는 중요한 암호화 알고리즘에 없어서는 안될 최적화 작업이 많이있었습니다. NP 경도는 최악의 경우 무증상 일 뿐이며 실제로 발생하는 문제에 대해 최적 또는 거의 최적의 솔루션을 생성 할 수 있습니다.
코드 InChaos

텍스트는 a정수는 아니지만 코드는 언급 합니다. 그 결과 텍스트의 "매우 효율적인"계산 결과의 정확성 이 궁금합니다 .
Andrew Morton

69

경우 의 자유롭게 사용할 C 버전은pow 어떤 표시, 그것은 당신이 기대 아무것도처럼 보이지 않는다. .NET 버전을 찾는 데 큰 도움이되지 않습니다. 해결해야 할 문제 (예 : 정수가있는 문제)는 수십 배 더 간단 하고 지수 가있는 몇 줄의 C # 코드에서 해결할 수 있기 때문 입니다. 알고리즘을 제곱하여 .


답변 주셔서 감사합니다. 첫 번째 링크는 Pow () 함수의 대규모 기술 구현을 기대하지 않아 놀랐습니다. Hans Passant의 대답은 .Net 세계에서도 마찬가지입니다. 제곱 알고리즘 링크에 나열된 기술 중 일부를 사용하여 문제를 해결할 수 있다고 생각합니다. 다시 감사합니다.
Pawan Mishra

2
이 코드가 효율적이라고 생각하지 않습니다. 30 개의 지역 변수는 모든 레지스터를 충돌시킵니다. 나는 그것이 ARM 버전이라고 가정하지만 x86 30에서는 메소드의 로컬 변수가 훌륭합니다.
Alex Zhukovskiy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.