glibc 实现方法简述:
对于任意底数的对数,可以利用换底公式:
logkx=lnklnx(1)方程 (1) 中的 lnk 是常数,因此对于任意底数的对数,我们只需计算 lnx。
在高等数学中,我们学过下面的泰勒级数:
ln(1+x)=x−21x2+31x3−41x4+⋯=k=1∑∞k(−1)k−1x(2)假设要求 lnx,自然可以令 t=x−1,于是:
ln(x)=ln(1+t)=k=1∑∞k(−1)ktk(2)然而,在实际计算中通常不可能展开到无穷项。若展开到第 n 项,带皮亚诺余项的泰勒级数可以表示为:
ln(1+t)=t−21t2+31t3−41t4+⋯=k=1∑nk(−1)ktk+o(tn)(3)注意这里的皮亚诺余项是 o(tn),代表误差项。若 t>1(即 x>2),随着 t 或 x 增大,上面公式的误差可能会变得相当大。
不过,还有另一个公式可用:
lnx=2(x+1x−1+31(x+1x−1)3+51(x+1x−1)5+…)=2k=0∑∞2k+11(x+1x−1)2k+1(4)以下是对方程 (4) 的非正式证明:
在方程 (2) 中分别用 x1 与 −x1 替换 x:
ln(1+x1)ln(1−x1)=ln(xx+1)=k=1∑∞k(−1)kxk1=−k=1∑∞kxk1
注意:
ln(xx−1)⟹−ln(xx−1)⟹ln(x−1x)=−k=1∑∞kxk1=k=1∑∞kxk1=k=1∑∞kxk1
接着:
ln(xx+1)+ln(x−1x)=ln(x−1x+1)k=1∑nkxk(−1)k+k=1∑nkxk1=2k=0∑n2k+11x2k+11
由此可得:
ln(x−1x+1)=2k=0∑n2k+11x2k+11令 z=(x−1)(x+1),则 x=(z+1)(z−1)。将其代入上式即得方程 (4)。
这个公式的好处在于,若展开到第 n 项:
lnx=2k=0∑n2k+11(x+1x−1)2k+1+o((x+1x−1)2k+1)(5)其余项为 o((x+1)2k+1(x−1)),由于 limx→+∞(x+1x−1)2k+1=1,该余项保证误差保持在 1 的范围之内。
然而这仍然不够,因为误差为 1 还是太大了。根据上面余项的分析,若要更高精度,x 的值应当更接近 1。
考虑到 IEEE 754 浮点标准,我们知道任何浮点数在计算机中都被表示为:
x=1.xxx×2exp这意味着我们可以通过位运算直接获得指数(exp)与小数部分(1.xxx)。
由于浮点数已经是这种形式,我们可以直接利用小数部分 1.xxx。若用前面的公式计算 ln(1.xxx),就等价于计算 ln(2expx)。根据对数公式:
ln(2expx)=lnx−exp×ln2因此:
lnx=ln(2expx)+exp×ln2至此就完成了 lnx 的计算,等价于计算任意底数的对数。
不过这只是一个总体概述,glibc 在实现中采用了各种提升精度的技巧。