php – 使用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),$iscale); $lastResult = $result; $result = bcadd($fraction,$result,$iscale); } while($result !== $lastResult); return bcmul('2',$scale); } 但这需要5.7秒来运行bcln(100)(自然对数为100,小数点后10位).此外,对于更多的小数位,它并不总是准确的.有更好的算法吗? 对于该特定运行,需要573次迭代来确定结果. 解决方法
你需要一个任意长度的字符串作为答案吗?或者您需要任意精度或任意指数大小?
或者……双精度浮点答案(返回值)就足够了;鉴于我们“只”使用了一些“任意”大小的对数? 双精度浮点数具有11位有符号指数:因此,如果您的大数字字符串的长度≤1022位≈307十进制数字(所以字符串长度为306个字符,包括小数点),您是安全的!更准确地说,如果得到的十进制指数的绝对值≤307,则应该是安全的.你需要更大的指数吗? (换句话说,我想:你是在使用现实世界的数字还是理论/纯数学?) 为什么不使用一些字符串处理,以及一些简单的浮点日志算法?对于任何真实世界的数字,这应该非常快…… function bclog10($n){ //←Might need to implement some validation logic here! $pos=strpos($n,'.'); if($pos===false){ $dec_frac='.'.substr($n,15);$pos=strlen($n); }else{ $dec_frac='.'.substr(substr($n,$pos).substr($n,$pos+1),15); } return log10((float)$dec_frac)+(float)$pos; } 您可以使用一些众所周知的对数算法转换基数: function bclogn($n,$base=M_E){//$base should be float: default is e return bclog10($n)*log(10)/log($base); } 我已经测试了这些功能,它们适合我,我提供的例子;提供与Windows 10计算器完全相同的答案,达到PHP使用的双精度算术的极限. 如果你实际上需要超过15位精度和超过307的十进制指数,你可能能够实现自己的“BigFloat”类对象,并以某种方式使用标准的内置浮点函数构建其方法分而治之的方法!那么也许,我们可以将其作为任意精度浮点对数算法的基础,通过将其与上述函数/技术相结合.您可能需要考虑在math.stackexchange.com与人们协商,以了解更多关于这是否可行的方法. 主要编辑:第二次尝试…… 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,15);$pos=-1*$neg; }else{ $dec_frac='.'.substr($abs.$fre.$frc,15); } return log10((float)$dec_frac)+(float)$pos+($esgn*$eexp); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |