| 
注册时间2007-12-28最后登录1970-1-1威望 星金币 枚贡献 分经验 点鲜花 朵魅力 点上传 次下载 次积分12787在线时间 小时 
 | 
 
 发表于 2008-10-8 11:45:26
|
显示全部楼层 
| 下面给出两个计算 double数自然对数的函数,精度可达到12位10进制数的版本。第一个函数是Ln_2,第二个函数是Ln_3( 实际上我写了三个函数,另外的一个是Ln_1,这个函数效率较低,没有给出)。对于任意双精度浮点数,Ln_3只需要做11次浮点乘除法,就可以达到12位以上的精度。
主要算法:
  若$ 1 复制代码typedef unsigned short WORD;
typedef unsigned long DWORD;
#define LN2 0.69314718055994530941723212145818 //log(2)
#define DIG_NUM 12 //精确到12位有效数字
//#define DIG_NUM 15 //精确到15位有效数字
static double pow2[]=
{
 1.3407807929942597e+154,
 1.1579208923731620e+077,
 3.4028236692093846e+038,
 1.8446744073709552e+019,
 4.2949672960000000e+009,
 6.5536000000000000e+004,
 2.5600000000000000e+002,
 1.6000000000000000e+001,
 4.0000000000000000,
 2.0000000000000000,
 1.4142135623730951,
 1.1892071150027210,
 1.0905077326652577, 
 1.0442737824274138,
 1.0218971486541166,
 // 测试表明,当查表时查到 1.0218971486541166 这一项时,在其后
 // 阶段,当精度达到10^-12时,应用泰勒公式,只需要计算3项,
 // 即x, x^3/3, x^5/5,
 // 从而使得整个计算过程达到最优,至多只需要11次浮点乘除法,具体算法见
 // 函数Ln_3
 
 
 1.0108892860517005,
 1.0054299011128027,
 1.0027112750502025,
 1.0013547198921082,
 1.0006771306930664
};
static double pow2_exp[]=
{
 5.1200000000000000e+002,
 2.5600000000000000e+002,
 1.2800000000000000e+002,
 6.4000000000000000e+001,
 3.2000000000000000e+001,
 1.6000000000000000e+001,
 8.0000000000000000e+000,
 4.0000000000000000e+000,
 2.0000000000000000e+000,
 1.0000000000000000e+000,
 5.0000000000000000e-001,
 2.5000000000000000e-001,
 1.2500000000000000e-001,
 6.2500000000000000e-002,
 3.1250000000000000e-002,
 1.5625000000000000e-002,
 7.8125000000000000e-003,
 3.9062500000000000e-003,
 1.9531250000000000e-003,
 9.7656250000000000e-004
};
short GetExpBase2(double a) // 获得 a 的阶码
{
    // 按照IEEE 754浮点数格式,取得阶码,仅仅适用于Intel 系列 cpu 
    WORD *pWord=(WORD *)(&a)+3;
    WORD rank = ( (*pWord & 0x7fff) >>4 );
    return (short)(rank-0x3ff);
}
double GetMantissa(double a) // 获得 a 的 尾数
{
    // 按照IEEE 754浮点数格式,取得尾数,仅仅适用于Intel 系列 cpu 
    WORD *pWord=(WORD *)(&a)+3;
    *pWord &= 0x800f;  //清除阶码
    *pWord |= 0x3ff0;  //重置阶码
    return a;
}
// 对数计算公式:
//  若x>1,y=(x-1)/(x+1),则有
//  log(x)=2*(y+ y^3/3 + y^5/5 + y^7/7 + y^9/9 + ... y^(2k+1)/2k)(k为非负整数) 
// 计算log(x)的第2个版本,
// x>0
double Ln_2(double x)
{
 double r0,r1;
 double item;
 double s;
 int i;
 if (x<1)
 return -(Ln_2(1.0/x));
 for (r0=0,i=0;i<=9+5;i++)
 {
 if ( x>= pow2[i])
 {
 r0+= pow2_exp[i];
 x/= pow2[i];
 }
 }
 //此时x必然小于1.0218971486541
 r0 *= LN2;
 x=(x-1)/(x+1); 
 // 此时x小于0.010830001253374,2*x^7/7小于4.99265e-015,因此计算泰勒公式的前3项足够
 
 s=x*x;
 r1=item=x; //计算泰勒公式第1项
#if DIG_NUM ==12
 for (i=3;i<=5;i+=2)  //精确到12位有效数字
#else
 for (i=3;i<=7;i+=2)  //精确到15位有效数字
#endif
 {
 item *= s;
 r1 += item/i;  //泰勒公式其他3项
 }
 return r0+r1*2;
}
// 计算log(x)的第3个版本,速度更快,
// 精确到10^12,至多只需要11次浮点乘除法
// 要求:x必须是 IEEE754标准浮点格式,字节顺序是intel格式,在某些CPU可能不能正确工作
double Ln_3(double x)
{
 double r0,r1;
 double item;
 double s;
 int i;
 if (x<1)
 return -(Ln_3(1.0/x));
 
 r0= GetExpBase2(x);
 x= GetMantissa(x);
 
 for (i=9+1;i<=9+5;i++)
 {
 if ( x>= pow2[i])
 {
 r0+= pow2_exp[i]; x/= pow2[i];
 }
 }
 r0 *= LN2;
 x=(x-1)/(x+1); 
 s=x*x;
 r1=item=x; //计算泰勒公式第1项
#if DIG_NUM ==12
 for (i=3;i<=5;i+=2)  //精确到12位有效数字
#else
 for (i=3;i<=7;i+=2)  //精确到15位有效数字
#endif
 {
 item *= s;
 r1 += item/i;  //泰勒公式其他2项
 }
 return r0+r1*2;
}
 | 
 |