/*
###########################################################################
This function computes the arbitrary-precision Nth root of a given any
numerical string X, rounded to any specified number of decimals.
----------
ARGUMENTS:
N = Root to be iterated = Integer 1, 2, 3, 4, ..., etc.
X = Numerical string for which we seek the Nth root.
Decimals = Number of decimals at which to round off the result
if exact resolution is not possible.
Based on the following Nth root of X iteration algorithm.
-----------------------
Nth ROOT of X ITERATION
Let:
N = Root to be iterated (2=Square root, 3=Cube root, etc.)
a = current approximation to Nth root of (X)
Initial approximation = Normal native machine precision value
b = next generation approximation computed from current (a)
p = Precision limit.
10^(-(decimals + 1))
If we want 16 decimals of
precision, then we set:
p = 10^(-(16 + 1))
= 10^(-17)
= 10E-17
= 0.00000000000000001
At the end of each iteration, the resulting value of (b) becomes the
new value of (a) for the next cycle and the process is repeated until
the desired precision level is reached.
The iteration process continues while (abs(b-a) >= p)
When the difference abs(b-a) < p, then we are
done and the current value of (b) is returned.
---------------------------------
Nth ROOT of X ITERATION ALGORITHM
GIVEN (N, X, a, Decimals)
START
c = 1
n = N - 1
p = 10^(-(Decimals + 1))
LOOP_WHILE (c >= p)
{
b = (a + X/(a^n)) / n
c = abs(b - a)
a = b
}
return b
end
ERRORS:
FALSE is returned if either (N, X) argument is non-numeric .
NO DEPENDENCIES
###########################################################################
*/
function BC_Nth_Root ($N, $X, $Decimals=16)
{
$x = trim($X);
$N = trim($N);
if (!Is_Numeric($x) or !Is_Numeric($N))
{
return FALSE;
}
$N = floor(abs($N));
// Check for odd root value with negative argument, in which
// case, the sign of the argument is preserved in the root.
$sign = ''; if ($x < 0) {$x = Str_Replace('-', '', $x); $sign = '-';}
// --------------------------------------------------
// Generate 1st approximation = Native machine value.
$a = SPrintF("%1.16f", pow($x, 1/$N));
$n = floor(abs($N)) - 1;
$d = floor(abs($Decimals));
$D = 2*$d;
/* ---------------------------------------------------
Loop to iterate root value (limit = 50 iterations).
This should never get anywhere near 50. This limit
prevents a total lockup if something goes wrong.
*/
for ($i=1; $i <= 50; $i++)
{
// --------------------------------------------------------------
// Compute next approximation (b) from current approximation (a).
$b = bcDiv(bcAdd(bcMul($n,$a,$D),bcDiv($x,bcpow($a,$n,$D),$D),$D),$N,$D);
/* ------------------------------------------
If (a == b) to precision limit, then done.
Otherwise, execute another loop cycle.
*/
if (bcComp($a,$b,$D) == 0) {break;} else {$a = $b;}
}
// Done. Return root value rounded to specified number of decimals.
$w = $sign.bcAdd($b, '0.'.Str_Repeat(0,$d).'5', $d);
if ($w == bcAdd('0', $w))
{
$w = Rtrim(Rtrim($w, '0'), '.');
}
return $w;
} // End of BC_Nth_Root (...)