本帖最后由 明天自然醒 于 2023-7-11 20:39 编辑
你会发现你在论坛上,支持库里都找不到支持
- ln(自然对数)/ lg(以10位底的对数函数)
- exp(以自然对数为底的指数函数)
- pow(求次方)
以上这几个数学基本函数,可能由于精度和运算速度的问题,我猜测作者没有开发。
所以,本帖旨在缩小精度,速度不保证的情况下,提出上述三个函数。采用的是泰勒级数展开法,可能收敛比较慢,取得是小数部分的或参数优化,所以精度稍微有保障(统一了次数)
*意思是存在一定的误差,工程上允许的误差!
在关于lnx的函数原理中,我构造了两种函数,Remez函数是借鉴了C语言的核心,反反复复折腾了好久,有能力的可以参考原作品,或者参考Windows计算器的开源版,在GitHub上有发布。
标准方案,这套方案是 1993 年由 Sun Microsystems 正式写入 C 标准库的方案,函数为 double ieee754log(double x)(ieee754: IEEE二进制浮点数算术标准)。但这套方法为了性能的苛刻而写得过于复杂.
所有的对数函数计算核心都是利用(泰勒级数)然后多项式求和计算结果。为了性能或者精度的要求可能会对展开后的求和式子做进一步优化,最终会封装一个 ln 函数出来。其余的对数函数都是使用换底公式来套 ln 函数做的最底层实现,随着大量图形运算的需求提升,ln函数实现得好不好直接决定你电脑快不快。
[C] 纯文本查看 复制代码
#include "fdlibm.h"
#ifdef __STDC__
static const double
#else
static double
#endif
ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */
ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */
two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */
Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */
Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */
Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */
Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */
Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */
Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */
Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
static double zero = 0.0;
#ifdef __STDC__
double __ieee754_log(double x)
#else
double __ieee754_log(x)
double x;
#endif
{
double hfsq,f,s,z,R,w,t1,t2,dk;
int k,hx,i,j;
unsigned lx;
hx = __HI(x); /* high word of x */
lx = __LO(x); /* low word of x */
k=0;
if (hx < 0x00100000) { /* x < 2**-1022 */
if (((hx&0x7fffffff)|lx)==0)
return -two54/zero; /* log(+-0)=-inf */
if (hx<0) return (x-x)/zero; /* log(-#) = NaN */
k -= 54; x *= two54; /* subnormal number, scale up x */
hx = __HI(x); /* high word of x */
}
if (hx >= 0x7ff00000) return x+x;
k += (hx>>20)-1023;
hx &= 0x000fffff;
i = (hx+0x95f64)&0x100000;
__HI(x) = hx|(i^0x3ff00000); /* normalize x or x/2 */
k += (i>>20);
f = x-1.0;
// 到这,第一步标记,得出 k, f, s 的值了
if((0x000fffff&(2+hx))<3) { /* |f| < 2**-20 */
if(f==zero) if(k==0) return zero; else {dk=(double)k;
return dk*ln2_hi+dk*ln2_lo;}
R = f*f*(0.5-0.33333333333333333*f);
if(k==0) return f-R; else {dk=(double)k;
return dk*ln2_hi-((R-dk*ln2_lo)-f);}
}
s = f/(2.0+f);
dk = (double)k;
z = s*s;
i = hx-0x6147a;
w = z*z;
j = 0x6b851-hx;
t1= w*(Lg2+w*(Lg4+w*Lg6));
t2= z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7)));
i |= j;
R = t2+t1;
if(i>0) {
hfsq=0.5*f*f;
if(k==0) return f-(hfsq-s*(hfsq+R)); else
return dk*ln2_hi-((hfsq-(s*(hfsq+R)+dk*ln2_lo))-f);
} else {
if(k==0) return f-s*(f-R); else
return dk*ln2_hi-((s*(f-R)-dk*ln2_lo)-f);
}
}
主要核心应该是:雷米兹算法 - 维基百科,自由的百科全书 (wikipedia.org)https://zh.wikipedia.org/wiki/%E9%9B%B7%E7%B1%B3%E8%8C%B2%E6%BC%94%E7%AE%97%E6%B3%95
Remez算法。
大数扩展函数.e
(24.69 KB, 下载次数: 46)
|