Piszę otoki dla rozszerzenia bcmath
i bug #10116 dotyczące bcpow()
jest szczególnie irytujące - to rzuca $right_operand
($exp
) do (native PHP, a nie dowolnej długości) integer, więc gdy spróbujesz obliczyć pierwiastek kwadratowy (lub dowolny inny root wyższy niż 1
) liczby, zawsze otrzymasz numer 1
zamiast poprawnego wyniku.Obliczenia zmiennoprzecinkowe Powers (PHP/BCMath)
zacząłem poszukiwania algorytmów, które pozwoliłyby mi obliczyć pierwiastkowanie szeregu i ja found this answer który wygląda całkiem solidne, tak naprawdę expanded the formula użyciu WolframAlpha i byłem w stanie poprawić jego prędkość o około 5%, przy jednoczesnym zachowaniu dokładności wyników.
Oto czysta realizacja PHP naśladując moje wykonanie bcmath i jego ograniczeń:
function _pow($n, $exp)
{
$result = pow($n, intval($exp)); // bcmath casts $exp to (int)
if (fmod($exp, 1) > 0) // does $exp have a fracional part higher than 0?
{
$exp = 1/fmod($exp, 1); // convert the modulo into a root (2.5 -> 1/0.5 = 2)
$x = 1;
$y = (($n * _pow($x, 1 - $exp))/$exp) - ($x/$exp) + $x;
do
{
$x = $y;
$y = (($n * _pow($x, 1 - $exp))/$exp) - ($x/$exp) + $x;
} while ($x > $y);
return $result * $x; // 4^2.5 = 4^2 * 4^0.5 = 16 * 2 = 32
}
return $result;
}
Powyższy seems to work greatwyjątkiem gdy 1/fmod($exp, 1)
nie daje liczbę całkowitą. Na przykład, jeśli $exp
jest 0.123456
jej odwrotne będą 8.10005
i wyniki pow()
i _pow()
będzie nieco różne (demo):
pow(2, 0.123456)
=1.0893412745953
_pow(2, 0.123456)
=1.0905077326653
_pow(2, 1/8)
=_pow(2, 0.125)
=1.0905077326653
Jak osiągnąć ten sam poziom dokładności przy użyciu "ręcznych" obliczeń wykładniczych?
To działa dokładnie tak, jak reklamowane. '_pow' 'zaokrągla' część ułamkową do najbliższej' 1/n'. Możesz zrobić to rekurencyjnie. Więc po obliczeniu '_pow (2, 0.125)', obliczysz '_pow (2,0.125-123456)' i tak dalej. –
Ah, teraz rozumiem. Więc czy bcmath nie ma 'wyrażeń' i' log' lub czy istnieją inne powody, dla których 'a^b = exp (b * log (a))' nie jest opcją? Rekurencja Jeffreya sugeruje oczywiście, że działa, ale jego prędkość może nie być satysfakcjonująca, jeśli potrzebujesz wielu '1/k' do reprezentowania wykładnika. Czy zapisywanie wykładnika jako liczby wymiernej 'n/d' i obliczanie' (a^n)^(1/d) 'opcji, czy też należy oczekiwać zbyt dużych wartości' n' i 'd'? Być może warto zbadać przybliżenie wykładnika przez liczbę wymierną z małym mianownikiem (kontynuacja ekspansji frakcji) i wykonanie reszty z rekurencją. –
@JeffreySax: Ah, widzę ... To jest bummer, ale nadal nie działa (http://codepad.org/eI4ykyQU) czy coś mi brakuje? –