计算浮点功率(PHP / BCMath)

时间:2012-05-09 19:49:52

标签: php algorithm math pow bcmath

我为bcmath扩展程序编写了一个包装器,而关于bcpow() bug #10116 特别令人讨厌 - 它会强制转换{{1} ($right_operand)到(本机PHP,而不是任意长度)整数,所以当你尝试计算一个数字的平方根(或任何其他高于$exp的根)时,你总是会结束使用1代替正确的结果。

我开始搜索算法,这些算法可以让我计算一个数字的第n个根,而found this answer看起来很稳固,我实际上expanded the formula使用WolframAlpha并且我能够改进它&#39 ; s速度提高约5%,同时保持结果的准确性。

这是一个纯粹的PHP实现,模仿我的BCMath实现及其局限性:

1

上述seems to work great 除非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; } 不产生整数。例如,如果1 / fmod($exp, 1)$exp,则其倒数为0.123456,而8.10005pow()的结果将略有不同({{3} }):

  • _pow() = pow(2, 0.123456)
  • 1.0893412745953 = _pow(2, 0.123456)
  • 1.0905077326653 = _pow(2, 1 / 8) = _pow(2, 0.125)

如何使用"手册"达到相同的准确度?指数计算?

1 个答案:

答案 0 :(得分:5)

找到(正)数a的n th 根的所用算法是用于求零的牛顿算法

f(x) = x^n - a.

只涉及以自然数作为指数的权力,因此很容易实现。

使用指数0 < y < 1计算幂y,其中1/n不是具有整数n的{​​{1}}形式,这更复杂。做模拟,解决

x^(1/y) - a == 0

将再次涉及用非积分指数计算幂,这是我们试图解决的问题。

如果y = n/d合理且小分母d,则可以通过计算轻松解决问题

x^(n/d) = (x^n)^(1/d),

但是对于大多数理性0 < y < 1,分子和分母相当大,而中间x^n会很大,因此计算会占用大量内存并占用(相对)长时间。 (对于0.123456 = 1929/15625的示例指数,它不是太糟糕,但0.1234567会相当费力。)

计算一般理性0 < y < 1的权力的一种方法是写

y = 1/a ± 1/b ± 1/c ± ... ± 1/q

使用整数a < b < c < ... < q并对每个x^(1/k)进行乘法/除法。 (每个理性0 < y < 1都有这样的表示,而最短的表示通常不涉及很多术语,例如。

1929/15625 = 1/8 - 1/648 - 1/1265625;

在分解中仅使用加法导致具有更大分母的更长表示,例如

1929/15625 = 1/9 + 1/82 + 1/6678 + 1/46501020 + 1/2210396922562500,

因此需要更多的工作。)

通过混合方法可以实现一些改进,首先通过y的连续分数展开找到具有小分母的y的近似有理逼近 - 对于示例指数1929/15625 = [0;8,9,1,192]并使用前四个部分商产生近似10/81 = 0.123456790123... [注意10/81 = 1/8 - 1/648,最短分解成纯部分的部分和是收敛的] - 然后将剩余部分分解为纯部分。

然而,一般来说,这种方法导致为大n计算n th 根,如果最终结果的期望精度很高,这也是缓慢且占用大量内存的。< / p>

总而言之,实施explog并使用

可能更简单,更快捷
x^y = exp(y*log(x))