Szukałem wydajnego podejścia do obliczania b (powiedz a = 2
i b = 50
). Na początek postanowiłem rzucić okiem na implementację Math.Pow()
funkcji. Ale w .NET Reflector znalazłem tylko:
[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical]
public static extern double Pow(double x, double y);
Jakie są zasoby, w których mogę zobaczyć, co się dzieje w środku, gdy wywołuję Math.Pow()
funkcję?
InternalCall
zextern
modyfikatorem (ponieważ wydają się one być w konflikcie), zapoznaj się z pytaniem (i uzyskanymi odpowiedziami), które zamieściłem na ten temat.2^x
operacji, którax
jest liczbą całkowitą, wynikiem jest operacja przesunięcia. Więc może mógłbyś skonstruować wynik za pomocą mantysy2
i wykładnikax
.Odpowiedzi:
Oznacza to, że metoda jest faktycznie zaimplementowana w CLR, napisanym w C ++. Kompilator just-in-time konsultuje tabelę z wewnętrznie zaimplementowanymi metodami i bezpośrednio kompiluje wywołanie funkcji C ++.
Spojrzenie na kod wymaga kodu źródłowego dla CLR. Możesz to uzyskać z dystrybucji SSCLI20 . Został napisany wokół ramy czasowej .NET 2.0. Znalazłem implementacje niskiego poziomu, które lubią
Math.Pow()
być w dużej mierze dokładne w późniejszych wersjach CLR.Tabela odnośników znajduje się w clr / src / vm / ecall.cpp. Sekcja istotna dla
Math.Pow()
wygląda następująco:Wyszukiwanie „COMDouble” prowadzi do clr / src / classlibnative / float / comfloat.cpp. Oszczędzę ci kodu, po prostu sprawdź. Zasadniczo sprawdza przypadki narożne, a następnie wywołuje wersję CRT
pow()
.Jedynym interesującym szczegółem implementacji jest makro FCIntrinsic w tabeli. To wskazówka, że fluktuacja może implementować tę funkcję jako wewnętrzną. Innymi słowy, zastąp funkcję wywołania zmiennoprzecinkową instrukcją kodu maszynowego. To nie jest przypadek
Pow()
, nie ma instrukcji FPU. Ale z pewnością w przypadku innych prostych operacji. Warto zauważyć, że dzięki temu matematyka zmiennoprzecinkowa w języku C # jest znacznie szybsza niż ten sam kod w języku C ++, sprawdź tę odpowiedź z tego powodu.Nawiasem mówiąc, kod źródłowy dla CRT jest również dostępny, jeśli masz pełną wersję katalogu Visual Studio vc / crt / src. Trafisz jednak na mur
pow()
, Microsoft kupił ten kod od Intela. Wykonywanie lepszej pracy niż inżynierowie Intela jest mało prawdopodobne. Chociaż tożsamość mojej licealnej książki była dwukrotnie szybsza, gdy jej spróbowałem:Ale nie jest to prawdziwy substytut, ponieważ kumuluje błąd z 3 operacji zmiennoprzecinkowych i nie radzi sobie z dziwnymi problemami domenowymi Pow (). Jak 0 ^ 0 i -Niekończoność podniesiona do dowolnej potęgi.
źródło
pow
jest niezwykle trudne do dokładnego wdrożenia, będąc funkcją transcendentalną (patrz Dylemat Twórcy Stołu ). Jest to o wiele łatwiejsze dzięki zintegrowanej mocy.Odpowiedź Hansa Passanta jest świetna, ale chciałbym dodać, że jeśli
b
jest liczbą całkowitą, toa^b
można ją bardzo skutecznie obliczyć z rozkładem binarnym. Oto zmodyfikowana wersja z Hacker's Delight Henry'ego Warrena :Zauważa, że ta operacja jest optymalna (wykonuje minimalną liczbę operacji arytmetycznych lub logicznych) dla wszystkich b <15. Również nie jest znane rozwiązanie ogólnego problemu znalezienia optymalnej sekwencji czynników do obliczenia
a^b
dla dowolnego b innego niż ekstensywne Szukaj. To trudny NP. Zasadniczo oznacza to, że rozkład binarny jest tak dobry, jak to tylko możliwe.źródło
a
jest liczbą zmiennoprzecinkową.a
byciu liczbą całkowitą, ale kod tak. W związku z tym zastanawiam się nad dokładnością wyniku „bardzo wydajnego” obliczenia tekstu.Jeśli dowolnie dostępna wersja C
pow
jest jakąkolwiek wskazówką, nie wygląda na nic, czego można by oczekiwać. Znalezienie wersji .NET nie przydałoby się zbytnio, ponieważ problem, który rozwiązujesz (tj. Ten z liczbami całkowitymi), jest o kilka rzędów wielkości prostszy i można go rozwiązać za pomocą potęgowania w kilku wierszach kodu C # algorytm kwadratu .źródło