Algorytm ten jest stałym podziałem dwóch niepodpisanych 16-bitowych wartości ułamkowych w [0,1). Najpierw oblicza wstępną 9-bitową aproksymację do odwrotności dzielnika za pomocą przeszukiwania tabeli, doprecyzowuje ją przy pomocy pojedynczej iteracji Newtona-Raphsona dla odwrotności, x i + 1: = xi = 0 * (2 - d * x i), w wyniku czego dokładność odwrotna do około 16 bitów, ostatecznie mnoży to przez dywidendę, dając 17-bitowy współczynnik w [0,2).
Przy wyszukiwaniu tabeli dzielnik jest najpierw normalizowany na [0,5, 1) przez zastosowanie współczynnika skalowania 2 z, oczywiście dywidenda musi być następnie skorygowana o taki sam współczynnik skali. Ponieważ odwrotność argumentów w [0.5, 1), będzie wynosić [1,2], bit całkowity odwrotności jest znany jako 1, więc można użyć 8-bitowych wpisów w tabeli, aby wytworzyć 1.8 punktu stałego odwrotność przez dodanie 0x100
(= 1). Powód 0x101
jest tutaj użyty, nie jest jasny; może to wynikać z wymogu, że ten krok zawsze zapewnia zawyżenie prawdziwej wzajemności.
Kolejne dwa kroki to dosłowne tłumaczenia iteracji Newtona-Raphsona dla odwrotności z uwzględnieniem stałych współczynników skali. Tak więc 0x2000000
reprezentuje 2.0. Kod używa 0x2000080
, ponieważ zawiera stałą zaokrąglania 0x80
(= 128) dla następnego podziału przez 256, używaną do przeskalowania wyniku. Następny krok również dodaje 0x00000080
jako stałą zaokrąglania dla podziału skalowania o 256. Bez skalowania byłoby to czyste mnożenie.
Ostateczne pomnożenie n*d
zwielokrotnia odwłok w d
z dywidendą w n
, aby uzyskać iloraz w 33 bitach. Ponownie, stała zaokrąglania 0x8000 jest stosowana przed dzieleniem przez 65536, aby przeskalować do właściwego zakresu, podając iloraz w formacie 1.16 stałoprzecinkowym.
Ciągłe przeskalowywanie jest typowe dla obliczeń stałoprzecinkowych, gdy próbuje się uzyskać możliwie jak największe wyniki pośrednie, aby zmaksymalizować dokładność końcowego wyniku. Nieco niezwykłe jest to, że zaokrąglanie jest stosowane we wszystkich arytmetyce pośredniej, a nie tylko w ostatnim kroku. Być może konieczne było osiągnięcie określonego poziomu dokładności.
Funkcja ta nie jest jednak dokładna, co prawdopodobnie wynika z niedokładności wstępnego przybliżenia. We wszystkich nie wyjątkowych przypadkach 2,424,807,756 pasuje do prawidłowo zaokrąglonego 1,16 punktu stałego, 780,692,403 ma błąd równy 1 ulp, 15 606,093 ma błąd 2-ulp, a 86 452 ma błąd 3-ulp. W szybkim eksperymencie, maksymalny błąd w początkowym przybliżeniu u
wynosił 3,89e-3. Poprawione wyszukiwanie w tabeli zmniejsza maksymalny błąd względny w u
do 2.85e-3, redukując, ale nie eliminując błędów 3-ulp w wyniku końcowym.
Jeśli chcesz spojrzeć na konkretnym przykładzie, należy rozważyć h
= 0,3 (0x4ccd
) podzielonej przez SZ3
= 0,2 (0x3333
). Następnie z
= 2, a więc d
= 0,2 * 4 = 0,8 (0xcccc
). To prowadzi do u
= 1,25 (0x140
). Ponieważ oszacowanie jest dość dokładne, spodziewamy się, że (2 - d * u) będzie blisko 1, a w rzeczywistości d
= 1.000015 (0x10001
). Udoskonalona wzajemność wychodzi na d
= 1.250015 (0x14001
), a iloraz wynosi zatem n
= 1,500031 (0x18002
).
Spróbuj http://codereview.stackexchange.com/ – OldProgrammer
Implementuje [podział Newtona-Raphsona] (https://en.wikipedia.org/wiki/Division_algorithm#Newton.E2.80.93Raphson_division) (link do Wikipedii). –
@OldProgrammer To pytanie jest nie na temat do przeglądu kodu i jest na drodze do zamknięcia - Przegląd kodu nie robi "wyjaśnienia kodu" lub "dlaczego/jak to działa." –