나는 b 를 계산하기위한 효율적인 접근법을 찾고있었습니다 (예 : a = 2
및 b = 50
). 작업을 시작하기 위해 Math.Pow()
기능 구현을 살펴보기로 결정했습니다 . 그러나 .NET Reflector 에서 내가 찾은 것은 다음과 같습니다.
[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern double Pow(double x, double y);
Math.Pow()
함수를 호출 할 때 무슨 일이 벌어지고 있는지 확인할 수있는 리소스는 무엇 입니까?
답변
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는 모든 힘을 올렸습니다.
답변
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 문제입니다. 따라서 기본적으로 이진 분해는 얻을 수있는만큼 좋습니다.
답변
경우 의 자유롭게 사용할 C 버전은pow
어떤 표시, 그것은 당신이 기대 아무것도처럼 보이지 않는다. .NET 버전을 찾는 데 큰 도움이되지 않습니다. 해결해야 할 문제 (예 : 정수가있는 문제)는 수십 배 더 간단 하고 지수 가있는 몇 줄의 C # 코드에서 해결할 수 있기 때문 입니다. 알고리즘을 제곱하여 .