/*
############################################################################
This function computes the arbitrary-precision natural logarithm for any
positive (x) argument to up to 100 decimals.
SPECIAL CASE:
If (x) is a decimal value (0 < x < 1), then Ln(x) = -Ln(1/x)
ERRORS:
Returns FALSE if non-numeric or if (x <= 0).
NO DEPENDENCIES
############################################################################
*/
function BC_Ln ($xArgStr, $Decimals=16)
{
$x = trim($xArgStr);
if (StrPos($x, '-') !== FALSE or $x <= 0 or !Is_Numeric($x))
{return FALSE;}
$q = trim($Decimals);
$Q = 100;
if ($q > $Q) {$q = $Q;}
if ($q <= 0) {$q = 1;}
// -------------------------------------------------------
// Handle case of fractional (x) values where (0 < x < 1).
$sign = ''; if ($x > 0 and $x < 1) {$sign = '-'; $x = bcDiv('1', $x, $Q);}
// -------------------------------------------------------------------------------
// Natural logarithm of 10 to 110 decimals (used as internal conversion constant).
$LN_10 = '2.30258509299404568401799145468436420760110148862877297603332790096757260967735248023599720508959829834196778403';
/* -----------------------------------------
Determine power of 10 in positive (x) and
where to place the decimal in the result.
*/
$dpos = 1;
if($x >= 10)
{
$dpos = floor(log10($x) + 1);
$x = Str_Replace('.', '', $x);
$x = substr($x,0,1).'.'.substr($x,1, StrLen($x));
}
$PrecisionLimit = '0.'.Str_Repeat('0', $q).'1';
$n = $term = 1;
$sum = 0;
$k = bcDiv(bcSub($x, '1', $Q), $x, $Q);
while (abs($term) > $PrecisionLimit)
{
$numer = bcpow($k, $n, $Q);
$denom = $n;
$term = bcDiv($numer, $denom, $Q);
$sum = bcAdd($sum, $term, $Q);
$n++;
}
$sum = bcAdd($sum, bcMul($dpos-1, $LN_10, $Q), $Q);
// ----------------------------------------------
// Round the sum to the given number of decimals.
return $sign.bcAdd($sum, Str_Replace('1','5', $PrecisionLimit), $q);
} // End of BC_Ln(...)