带bcmath的快速任意精度对数 [英] Fast arbitrary-precision logarithms with bcmath

查看:107
本文介绍了带bcmath的快速任意精度对数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这就是我所拥有的

function bcln($n, $scale=10) {
    $iscale = $scale+3;
    $result = '0.0';
    $i = 0;

    do {
        $pow = (1 + (2 * $i++));
        $mul = bcdiv('1', $pow, $iscale);
        $fraction = bcmul($mul, bcpow(bcsub($n, '1', $iscale) / bcadd($n, '1.0', $iscale), $pow, $iscale), $iscale);
        $lastResult = $result;
        $result = bcadd($fraction, $result, $iscale);
    } while($result !== $lastResult);

    return bcmul('2', $result, $scale);
}

但是运行bcln(100)(100的自然对数,十进制小数位)需要5.7秒.此外,对于更多的小数位,它并不总是准确的.有更好的算法吗?

But this takes 5.7 seconds to run bcln(100) (natural log of 100, 10 decimal places). Furthermore, it's not always accurate for more decimal places. Is there a better algorithm?

对于该特定运行,需要573次迭代才能确定结果.

For that specific run, it takes 573 iterations to settle on the result.

推荐答案

您是否需要一个任意长度的字符串作为答案?还是需要任意精度或任意指数大小? 或者……双精度浮点答案(返回值)就足够了;假设我们仅"使用具有多个"任意"大小的对数?

Do you require an arbitrary-length string as an answer? Or do you require arbitrary precision, or arbitrary exponent size? Or… would a double-precision floating-point answer (return-value) suffice; given that we're "only" working with the logarithm of a number of "arbitrary" size?

双精度浮点数具有11位带符号的指数:因此,如果大数字字符串的长度≤1022位≈307个十进制数字(因此字符串长度为306个字符,包括小数点),则是安全的!更准确地说,如果所得的十进制指数的绝对值≤307,则应该是安全的.您是否需要更大的指数? (我想换句话说:您是在处理真实数字还是理论/纯数学?)

Double precision floating point numbers have an 11-bit signed exponent: therefore, if your big number string has a length of ≤1022 bits ≈ 307 decimal digits (so string length of 306 characters including the decimal point), you are safe! More accurately, you should be safe if the absolute value of the resulting decimal exponent is ≤307. Do you need bigger exponents than that? (I suppose in other words: are you working with real-world numbers or theoretical/ pure mathematics?)

为什么不仅仅使用一些字符串处理以及一些简单的浮点对数算法?对于任何现实世界的数字,这应该非常快 ...

Why not just use some string processing, along with some simple floating-point log arithmetic? This should be very fast, for any real-world numbers…

function bclog10($n){
    //←Might need to implement some validation logic here!
    $pos=strpos($n,'.');
    if($pos===false){
        $dec_frac='.'.substr($n,0,15);$pos=strlen($n);
    }else{  $dec_frac='.'.substr(substr($n,0,$pos).substr($n,$pos+1),0,15);
    }
    return log10((float)$dec_frac)+(float)$pos;
}

您可以使用一些众所周知的对数算法来转换基数:

You can convert the base using some well-known log arithmetic:

function bclogn($n,$base=M_E){//$base should be float: default is e
    return bclog10($n)*log(10)/log($base);
}

对于我提供的示例,我已经测试了这些功能,它们对我有用.给出的答案与Windows 10计算器完全相同,但要达到PHP使用的双精度算术的极限.

I have tested these functions and they work for me, for the examples I supplied; giving exactly the same answers as the Windows 10 calculator, up to the limits of double-precision arithmetic as used by PHP.

如果您实际上需要15位以上的精度和307以上的十进制指数,则可以实现自己的"BigFloat"类对象,并以某种方式从标准的内置浮动对象中构建其方法,使用分治法的点函数!然后,也许可以通过将其与上述函数/技术相结合,将其用作任意精度浮点对数算法的基础.您可能要考虑与 math.stackexchange.com 的人进行协商,以了解有关这是否可行的更多信息.

If you actually need more than 15 digits of precision and more than 307 for the decimal exponent, you might be able to implement your own "BigFloat" class object, and somehow build its methods out of the standard built-in floating-point functions using a divide-and-conquer approach! Then perhaps, we might use that as the basis of an arbitrary-precision floating-point logarithm algorithm, by combining this with the functions/techniques described above. You might want to consider consulting with the people at math.stackexchange.com, to find out more about whether this could be a feasible approach.

主要第二次尝试...

function bclog10($n){//By Matthew Slyman @aaabit.com
    $m=array();// ↓ Validation, matching/processing regex…
    preg_match('/^(-)?0*([1-9][0-9]*)?(\.(0*))?([1-9][0-9]*)?([Ee](-)?0*([1-9][0-9]*))?$/',$n,$m);
    if(!isset($m[1])){throw new \Exception('Argument: not decimal number string!');}
    $sgn=$m[1];if($sgn==='-'){throw new \Exception('Cannot compute: log(<⁺0)!');}
    $abs=$m[2];$pos=strlen($abs);
    if(isset($m[4])){$fre=$m[4];}else{$fre='';}$neg=strlen($fre);
    if(isset($m[5])){$frc=$m[5];}else{$frc='';}
    if(isset($m[7])){$esgn=$m[7]==='-'?-1:1;}else{$esgn=1;}
    if(isset($m[8])){$eexp=$m[8];}else{$eexp=0;}
    if($pos===0){
        $dec_frac='.'.substr($frc,0,15);$pos=-1*$neg;
    }else{  $dec_frac='.'.substr($abs.$fre.$frc,0,15);
    }
    return log10((float)$dec_frac)+(float)$pos+($esgn*$eexp);
}

这篇关于带bcmath的快速任意精度对数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆