请问如何快速计算出以二为底对数?该数在0到1之间,要求计算出的精度在小数点4位。谢谢
谢谢。 问题点数:50、回复次数:9Top
1 楼happy__888([顾问团]寻开心 www.e-jjj.com)回复于 2005-09-06 10:02:47 得分 25
用展开公式逼近吧
ln(1-x) = -(x + x^2/2 + x^3/3 + x^4/4 ....)
x-1 1 x-1 1 x-1
lnx = 2* (---- + - -------^3 + - --------^5 .........)
x+1 3 x+1 5 x+1Top
2 楼liangbch(宝宝)回复于 2005-09-06 10:46:48 得分 25
从8byte double 数中读出阶码k(k<0),再将此数 乘以 (2的-k次方)以转化为 1-2之间的数x1,然后就可以用上面的方法求出 ln(x1),则结果为 y= k + ln(x1)/ln(2).Top
3 楼happy__888([顾问团]寻开心 www.e-jjj.com)回复于 2005-09-06 11:32:33 得分 0
换个办法吧:
对于在0到1区间的 x, 令 y=1/x
我们计算出 log y来, 显然 log x = - log y
y的范围是 1 到 正无穷
浮点数在计算机当中的表述方式就是:
y = 1.m * 2^n
可以根据浮点数的内存结构,直接提出出来 n的数值
log y = n + log 1.m
提取出来n后,我们直接操作浮点数,把它规范化到1.m的格式
然后对于1到2区间的数值,建立一个跨度为0.0001的查询表
通过查询计算出 log1.m的数值
关于浮点数的内存结构可以从我的blog当中获取: blog.csdn.net/happy__888
jiejueTop
4 楼liangbch(宝宝)回复于 2005-09-06 15:09:17 得分 0
下面给出完整的程序:
#include "stdlib.h"
#include "stdio.h"
typedef unsigned short WORD;
#define LOG2E 1.4426950408889634073599246810019 //log2(e)
short GetExpBase2(double a) // 获得 a 的阶码
{
// 按照IEEE 754浮点数格式,取得阶码,仅仅适用于Intel 系列 cpu
WORD *pWord=(WORD *)(&a)+3;
return ( (*pWord & 0x7fff) >>4 )-0x3ff;
}
double GetMantissa(double a) // 获得 a 的 尾数
{
// 按照IEEE 754浮点数格式,取得尾数,仅仅适用于Intel 系列 cpu
WORD *pWord=(WORD *)(&a)+3;
*pWord &= 0x800f; //清除阶码
*pWord |= 0x3ff0; //重置阶码
return a;
}
// 必须 1.0 <=x <2.0
double my_ln(double x)
// 误差小于1e-5
{
double e1,e2,e3,e5,e7;
x-=1.0;
e1= x/(2+x);
e2=e1*e1;
e3=e1*e2;
e5=e3*e2;
e7=e5*e2;
return 2*(e1+ (1.0/3)*e3 +(1.0/5)*e5 + (1.0/7)*e7);
}
double log2(double x)
{
double f;
if (x<=0)
return 0; //负数不能求对数
short k= GetExpBase2(x);
f= GetMantissa(x);
return k + my_ln(f) * LOG2E;
}
int main(int argc, char* argv[])
{
double e0,e1,e2,e3;
e0=log2(1.9999999);
e1=log2(0.1);
e2=log2(0.01);
e3=log2(0.0005);
return 0;
}Top
5 楼liangbch(宝宝)回复于 2005-09-06 15:15:11 得分 0
函数 log2 不仅适用于 参数小于1的情况,也适用于任何大于0的浮点数,且可保证精确到4位小数,如果想得到更高的精度,请修改函数 my_ln ,多计算几项。Top
6 楼mysword(一怒拔剑)回复于 2005-09-06 16:00:58 得分 0
还可以按照手算对数的方法求到任意精度Top
7 楼happy__888([顾问团]寻开心 www.e-jjj.com)回复于 2005-09-06 16:06:56 得分 0
my_ln是利用ln的展开式进行计算的,精度是依靠展开式子的长度来保证的
最大误差发生在1.m当中的m接近1的时候,可以再加一段测试误差的代码:
在展开到e1的时候,最大误差是: 0.03
在展开到e3的时候,最大误差是: 0.0025
在展开到e5的时候,最大误差是: 0.00027
在展开到e7的时候,最大的误差是; 0.000018
展开的级数约高,进度约好,但是对应的计算越多
按照上述的方法,现在的计算速度已经比直接计算log(x) * s 慢了
s = 1.0 / log(2) 是一个常量。
在计算1.m的时候,用查表的速度更快,牺牲空间换取速度,当然精度越高,表越长Top
8 楼happy__888([顾问团]寻开心 www.e-jjj.com)回复于 2005-09-06 16:10:19 得分 0
在e5的时候,速度比log自身的运算慢3倍
即便是e1也是慢2倍的
速度慢,就达不到要求了
测试的方法,代码仿照 宝宝的前文,建立如下:
double s = 1.0f / log(2);
clock_t start, finish;
start = clock();
for ( int i=0; i<1000000; i++ ) p = log(1.9999) * s;
finish = clock();
double duration1 = (double)(finish - start);
start = clock();
for ( i=0; i<1000000; i++ ) p = log2(1.9999);
finish = clock();
double duration2 = (double)(finish - start);
printf("x=%lf, error=%lf\n", duration1, duration2);
因此应该是利用浮点数的内存结构,直接提取指数和小数的部分,然后查表为快
Top
9 楼happy__888([顾问团]寻开心 www.e-jjj.com)回复于 2005-09-06 16:42:43 得分 0
关于查表是建立一个1到2之间的数据表
根据前面的描述1.m后面的m数值可以从浮点数的内存结构当中直接的提取
提取出来后按照整数来理解它
根据表的长度,选择适当几位提取
表的长度也是2^n为好,这样就可以直接使用,避免了一些不必要的转换和计算
Top




