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


432

나는 b 를 계산하기위한 효율적인 접근법을 찾고있었습니다 (예 : a = 2및 b = 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.