打印

向各位请教一个开根号的算法

[复制链接]
12897|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
cgkdxx|  楼主 | 2007-8-6 08:25 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
沙发
cgkdxx|  楼主 | 2007-8-6 12:21 | 只看该作者

各位指点下吧 谢谢!!

使用特权

评论回复
板凳
xwj| | 2007-8-6 14:15 | 只看该作者

近似算法:右移一半的位,高位查表

使用特权

评论回复
地板
CGKDXX| | 2007-8-6 14:31 | 只看该作者

可否详细点?谢谢!!

右移4次,高4到低4? 然后高4位咋办?表怎么建的? 谢谢!!

使用特权

评论回复
5
zgl7903| | 2007-8-6 15:03 | 只看该作者

从LIB库里提取出来SQRT函数,反汇编一下,敲进去就可以了

使用特权

评论回复
6
jxb163| | 2007-8-6 15:23 | 只看该作者

用数学方法

1+3+。。。。(2N-1)=N*N   
倒过来用减法就可以拉N*N-1-3-。。。。一直减到它变为0或负值为止

使用特权

评论回复
7
CGKDXX| | 2007-8-6 16:14 | 只看该作者

jxb163大哥太神奇了!!

使用特权

评论回复
8
ningpanda| | 2007-8-6 20:32 | 只看该作者

比较好的方法

牛顿迭代法

使用特权

评论回复
9
gyt| | 2007-8-6 23:49 | 只看该作者

同意楼上

具体方法查一下书即可

使用特权

评论回复
10
wh6ic| | 2007-8-7 07:47 | 只看该作者

模拟手工开方的办法计算,适合位数比较长的.

一次移两位,16位的只用计算八次就得到结果了.

使用特权

评论回复
11
guoqi| | 2007-8-7 08:00 | 只看该作者

呵呵

二分法!

使用特权

评论回复
12
river1972| | 2007-8-7 09:34 | 只看该作者

同意楼上的

最简单就是二分法

使用特权

评论回复
13
懒人| | 2007-8-7 10:24 | 只看该作者

8 楼的好 牛顿迭代法速度快

使用特权

评论回复
14
cgkdxx|  楼主 | 2007-8-7 11:12 | 只看该作者

谢谢各位!!

譬如:40 
用jxb163的2n-1 可以确定在6 7 之间,6.5代入 得42.25  大了
                                则 6.25      39.0625 小
                                   6.375     40.6460 大
                                   6.3125    39.84
                                   6.3437    40.24
                                    ****
是不是就这样,直到允许误差时所得的值?
只是这小数一起乘,太麻烦了 呵呵
请各位指点一下 谢谢!!

使用特权

评论回复
15
zhiwei| | 2007-8-7 19:01 | 只看该作者

楼上想法不错

想增加精度就继续开,后面都是小数。我写过AVR的32bit汇编开方的子程序,你搜一下就能看到,当时用汇编是为了提高速度。

使用特权

评论回复
16
xwj| | 2007-8-8 06:35 | 只看该作者

转贴:[匠人的程序宝典]--单片机上的开方程序

[程序宝典]单片机上的开方程序

 
 
因为工作的需要,要在单片机上实现开根号的操作。目前开平方的方法大部分是用牛顿迭代法。我在查了一些资料以后找到了一个比牛顿迭代法更加快速的方法。不敢独享,介绍给大家,希望会有些帮助。 

1.原理 
因为排版的原因,用pow(X,Y)表示X的Y次幂,用B[0],B[1],...,B[m-1]表示一个序列, 
其中[x]为下标。 

假设: 
B[x],b[x]都是二进制序列,取值0或1。 
M = B[m-1]*pow(2,m-1) + B[m-2]*pow(2,m-2) + ... + B[1]*pow(2,1) + B[0]*pow 
(2,0) 
N = b[n-1]*pow(2,n-1) + b[n-2]*pow(2,n-2) + ... + b[1]*pow(2,1) + n[0]*pow 
(2,0) 
pow(N,2) = M 

(1) N的最高位b[n-1]可以根据M的最高位B[m-1]直接求得。 
设 m 已知,因为 pow(2, m-1) <= M <= pow(2, m),所以 pow(2, (m-1)/2) <= N <= 
pow(2, m/2) 
如果 m 是奇数,设m=2*k+1, 
那么 pow(2,k) <= N < pow(2, 1/2+k) < pow(2, k+1), 
n-1=k, n=k+1=(m+1)/2 
如果 m 是偶数,设m=2k, 
那么 pow(2,k) > N >= pow(2, k-1/2) > pow(2, k-1), 
n-1=k-1,n=k=m/2 
所以b[n-1]完全由B[m-1]决定。 
余数 M[1] = M - b[n-1]*pow(2, 2*n-2) 

(2) N的次高位b[n-2]可以采用试探法来确定。 
因为b[n-1]=1,假设b[n-2]=1,则 pow(b[n-1]*pow(2,n-1) + b[n-1]*pow(2,n-2), 
2) = b[n-1]*pow(2,2*n-2) + (b[n-1]*pow(2,2*n-2) + b[n-2]*pow(2,2*n-4)), 
然后比较余数M[1]是否大于等于 (pow(2,2)*b[n-1] + b[n-2]) * pow(2,2*n-4)。这种 
比较只须根据B[m-1]、B[m-2]、...、B[2*n-4]便可做出判断,其余低位不做比较。 
若 M[1] >= (pow(2,2)*b[n-1] + b[n-2]) * pow(2,2*n-4), 则假设有效,b[n-2] = 
1; 
余数 M[2] = M[1] - pow(pow(2,n-1)*b[n-1] + pow(2,n-2)*b[n-2], 2) = M[1] - 
(pow(2,2)+1)*pow(2,2*n-4); 
若 M[1] < (pow(2,2)*b[n-1] + b[n-2]) * pow(2,2*n-4), 则假设无效,b[n-2] = 
0;余数 M[2] = M[1]。 

(3) 同理,可以从高位到低位逐位求出M的平方根N的各位。 

使用这种算法计算32位数的平方根时最多只须比较16次,而且每次比较时不必把M的各位逐 
一比较,尤其是开始时比较的位数很少,所以消耗的时间远低于牛顿迭代法。 

2. 流程图 
(制作中,稍候再上) 

3. 实现代码 
这里给出实现32位无符号整数开方得到16位无符号整数的C语言代码。 


//------------------------------------------------------------------------------- - 
/****************************************/
/*Function: 开根号处理 */ 
/*入口参数:被开方数,长整型 */ /*出口参数:开方结果,整型 */ 
/****************************************/ 
unsigned int sqrt_16(unsigned long M) 
{
 unsigned int N, i; 
 unsigned long tmp, ttp; // 结果、循环计数 
 if (M == 0) // 被开方数,开方结果也为0 
     return 0; 
 N = 0;
 tmp = (M >> 30); // 获取最高位:B[m-1] 
 M <<= 2; 
 if (tmp > 1) // 最高位为1 
 { 
   N ++; // 结果当前位为1,否则为默认的0 
   tmp -= N; 
 } 
 for (i=15; i>0; i--) // 求剩余的15位 
 { 
   N <<= 1; // 左移一位 
   tmp <<= 2; 
   tmp += (M >> 30); // 假设 
   ttp = N; 
   ttp = (ttp<<1)+1; 
   M <<= 2; 
   if (tmp >= ttp) // 假设成立
   { 
     tmp -= ttp; N ++; 
   }
 }
 return N;

 

 

//本程序由xwj设计的UltraEdit脚本加亮显示,如需要脚本请访问我的Blog或联系xwjfile@21cn.com

使用特权

评论回复
17
xwj| | 2007-8-8 06:40 | 只看该作者

如果要保留小数那就建议用浮点,有个近似算法、只用了一

//这个论坛最早是sharks贴出来的

/**/
/* 来至 Quake 3 的源码 */
float CarmSqrt(float x){
    union
    {
        int intPart;
        float floatPart;
    } convertor;
    union
    {
        int intPart;
        float floatPart;
    } convertor2;
    convertor.floatPart = x;
    convertor2.floatPart = x;
    convertor.intPart = 0x1FBCF800 + (convertor.intPart >> 1);
    convertor2.intPart = 0x5f3759df - (convertor2.intPart >> 1);
    return 0.5f*(convertor.floatPart + (x * convertor2.floatPart));
}
//本程序由xwj设计的UltraEdit脚本加亮显示,如需要脚本请访问我的Blog或联系xwjfile@21cn.com

使用特权

评论回复
18
xwj| | 2007-8-8 06:46 | 只看该作者

快速开方算法网上讨论的很多的,大家先去搜索一下吧

原来上面说的叫做“卡马克算法”:

  求平方根的函数     
  更新:有人问这个算法的原理。其实原理很简单。就是牛顿迭代求根。卡马克算法牛X的地方就是他选了一个常数作为起始值。而这个起始值让他只用一次迭代就够了。   
    
  从这里看来的。QuakeIII自然就是传奇高手卡马克的杰作之一了。在有的CPU上,这个函数比普通的(float)(1.0/sqrt(x)快4倍!快的原因之一是用了一个神秘常数,0x5f3759df。普渡大学的Chris   Lomont在这篇论文里讨论了这个常数的意义,尝试用严格的方法推导出这个常数(他还提到有人认为这个函数是在NVidia工作过的Gary   Tarolli写的)。Chris推出的常数是0x5f37642f),和Q_rsqrt里的稍有不同,而且实际表现也稍有不如。卡马克到底怎么推出这个常数的就是谜了。这种高手不写书,实在可惜。   
  float   Q_rsqrt(   float   number   )   
  {   
      long   i;   
      float   x2,   y;   
      const   float   threehalfs   =   1.5F;   
    
      x2   =   number   *   0.5F;   
      y     =   number;   
      i     =   *   (   long   *   )   &y;     //   evil   floating   point   bit   level   hacking   
      i     =   0x5f3759df   -   (   i   >>   1   );   //   what   the   func?   
      y     =   *   (   float   *   )   &i;   
      y     =   y   *   (   threehalfs   -   (   x2   *   y   *   y   )   );   //   1st   iteration   
      //   y     =   y   *   (   threehalfs   -   (   x2   *   y   *   y   )   );   //   2nd   iteration,   this   can   be   removed   
    
      #ifndef   Q3_VM   
      #ifdef   __linux__   
          assert(   !isnan(y)   );   //   bk010122   -   FPE?   
      #endif   [/#]
      #endif   [/#]
      return   y;   
  }

使用特权

评论回复
19
cgkdxx|  楼主 | 2007-8-8 08:09 | 只看该作者

谢谢 !!

徐工也干通宵??  是不是等我也学会熬夜也是高手啦,嘿嘿  谢谢各位!!
等我弄好了也贴出来

使用特权

评论回复
20
救火车| | 2007-8-8 10:03 | 只看该作者

C语言的编译器都有自带的函数库。

直接用就行了。为什么要这么麻烦?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

192

主题

1126

帖子

0

粉丝