Вычисление степеней с плавающей запятой (PHP/BCMath)

Я пишу оболочку для расширения bcmathиошибка №10116относительно bcpow( )особенно раздражает — он приводит $right_operand( $exp) к (собственному PHP, не произвольной длины) целому числу, поэтому, когда вы пытаетесь вычислить квадрат корень (или любой другой корень выше, чем 1) числа вы всегда получите 1вместо правильного результата.

Я начал искать алгоритмы, которые позволили бы мне вычислить корень числа n-й степени, и я нашел этот ответ, который выглядит довольно солидно, я на самом деле расширил формулу, используя 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;
}

Вышеупомянутое работает отличнокроме случаев, когда 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

Как я могу достичь того же уровня точности, используя «ручные» экспоненциальные вычисления?

7
задан Community 23 May 2017 в 11:56
поделиться