Я пишу оболочку для расширения bcmath
и bug #10116 относительно bcpow()
особенно раздражает - это бросает $right_operand
($exp
) к (родной PHP, а не произвольной длины) целое число, поэтому при попытке вычислить квадратный корень (или любой другой корень выше, чем 1
) числа вы всегда в конечном итоге с 1
вместо правильного результата.Расчет с плавающей точкой Пауэрс (PHP/BCMath)
Я начал искать алгоритмы, которые позволили бы мне вычислить п-й корень из числа, и я found this answer, который выглядит довольно твердый, я на самом деле expanded the formula использованием WolframAlpha, и я был в состоянии улучшить его скорость примерно 5%, сохраняя при этом точность результатов.
Вот чистая реализация PHP имитируя мою реализацию BCMath и свои ограничения:
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;
}
выше seems to work greatкроме случаев, когда 1/fmod($exp, 1)
не дает целое. Например, если $exp
является 0.123456
, его обратное будет 8.10005
и исход pow()
и _pow()
будет немного другой (demo):
pow(2, 0.123456)
=1.0893412745953
_pow(2, 0.123456)
=1.0905077326653
_pow(2, 1/8)
=_pow(2, 0.125)
=1.0905077326653
Как достичь такого же уровня точности, используя «ручной» экспоненциальный расчет?
Это работает точно так, как рекламируется. '_pow' 'округляет' дробную часть до ближайшего' 1/n'. Вы можете сделать эту работу рекурсивно. Поэтому после вычисления '_pow (2, 0.125)', вы вычисляете '_pow (2,0.125-123456)' и так далее. –
А теперь я понимаю. Таким образом, bcmath не имеет 'exp' и' log' или есть другие причины, почему 'a^b = exp (b * log (a))' не является опцией? Рекурсия Джеффри предполагает, конечно, работу, но ее скорость может быть неудовлетворительной, если вам нужно много «1/k» для представления экспоненты. Записывает экспонента как рациональное число 'n/d' и вычисляет' (a^n)^(1/d) 'вариант, или слишком большие' n' и 'd' ожидаются? Возможно, стоит исследовать аппроксимацию экспоненты рациональным числом с малым знаменателем (разложение по длине фракции), а остальные - рекурсией. –
@JeffreySax: Ах, я вижу ... Это облом, но все еще не работает (http://codepad.org/eI4ykyQU) или я что-то упускаю? –