无心人
发表于 2009-2-12 17:54:17
FWAIT是
Causes the processor to check for and handle pending, unmasked, floating-point exceptions before proceeding.
无心人
发表于 2009-2-12 19:54:37
在386时代
FWAIT是等待FST FSTP FISTP写入内存完毕的指令
我想,P4时代应该不必了
无心人
发表于 2009-2-12 20:01:26
重新回到整数算法上来
假设bsr方法得到初始值
因为,初始值是个2^k类型的值
所以首次的牛顿迭代是无需乘法和除法的
现在的问题是,首次迭代的值和精确值的差距是多少
二次迭代是否能得到准确值??
如果不能得到
那不能得到精确值的输入是否有范围?
范围多大?
无心人
发表于 2009-2-12 20:19:23
网上找到的,似乎有用处
http://tech.ddvip.com/2008/08/121928805856259.html
为工作的需要,要在单片机上实现开根号的操作。目前开平方的方法大部分是用牛顿
迭代法。我在查了一些资料以后找到了一个比牛顿迭代法更加快速的方法。
1.原理
因为排版的原因,用pow(X,Y)表示X的Y次幂,用B,B,...,B表示一个序列,
其中为下标。
假设:
B,b都是二进制序列,取值0或1。
M = B*pow(2,m-1) + B*pow(2,m-2) + ... + B*pow(2,1) + B*pow
(2,0)
N = b*pow(2,n-1) + b*pow(2,n-2) + ... + b*pow(2,1) + n*pow
(2,0)
pow(N,2) = M
(1) N的最高位b可以根据M的最高位B直接求得。
设 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完全由B决定。
余数 M = M - b*pow(2, 2*n-2)
(2) N的次高位b可以采用试探法来确定。
因为b=1,假设b=1,则 pow(b*pow(2,n-1) + b*pow(2,n-2),
2) = b*pow(2,2*n-2) + (b*pow(2,2*n-2) + b*pow(2,2*n-4)),
然后比较余数M是否大于等于 (pow(2,2)*b + b) * pow(2,2*n-4)。这种
比较只须根据B、B、...、B便可做出判断,其余低位不做比较。
若 M >= (pow(2,2)*b + b) * pow(2,2*n-4), 则假设有效,b =
1;
余数 M = M - pow(pow(2,n-1)*b + pow(2,n-2)*b, 2) = M -
(pow(2,2)+1)*pow(2,2*n-4);
若 M < (pow(2,2)*b + b) * pow(2,2*n-4), 则假设无效,b =
0;余数 M = M。
(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 <<= 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;
}
无心人
发表于 2009-2-12 20:43:29
/****************************************/
/*Function: 开根号处理 */
/*入口参数:被开方数,长整型 */
/*出口参数:开方结果,整型 */
/****************************************/
#include <stdio.h>
#include <stdlib.h>
unsigned int sqrt_16(unsigned int M)
{
unsigned int N, i;
unsigned int tmp, ttp;// 结果、循环计数
if (M == 0) // 被开方数,开方结果也为0
return 0;
N = 0;
tmp = (M >> 30);// 获取最高位:B
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;
}
int main(void)
{
unsigned int M, N;
printf("Input M:");
scanf("%u", &M);
N = sqrt_16(M);
printf("\nSQRT(%u) = %u\n", M, N);
return 0;
}
写了个测试程序
测试了几个例子
E:\MinGW\MSYS\math>sqrt
Input M:1023
SQRT(1023) = 31
E:\MinGW\MSYS\math>sqrt
Input M:4000000000
SQRT(4000000000) = 63245
E:\MinGW\MSYS\math>sqrt
Input M:0
SQRT(0) = 0
E:\MinGW\MSYS\math>sqrt
Input M:1
SQRT(1) = 1
E:\MinGW\MSYS\math>sqrt
Input M:23
SQRT(23) = 4
E:\MinGW\MSYS\math>sqrt
Input M:400000000
SQRT(400000000) = 20000
gxqcn
发表于 2009-2-12 21:21:49
很奇怪的现象:
虽说我的43#代码够简洁,
但测试结果速度并不理想,基本上比无心人和liangbch写的函数要慢一倍。
我又测试了HugeCalc内部采用的纯C/C++版本(不含浮点计算),
虽然语句比较多,还有跳转语句(不见得编译后就有跳转指令),
但测试起来速度反而比用fsqrt的都快很多很多!
不信大家可以测试测试:UINT32 Sqrt( UINT32 n )
{
UINT32 t;
UINT32 ret = 0;
#define SQRT_CORE(x) \
t = ret + (1UL<<(x)); \
ret >>= 1; \
if ( n >= t ) \
{ \
n -= t; \
ret += (1UL<<(x));\
}
SQRT_CORE(30);
SQRT_CORE(28);
SQRT_CORE(26);
SQRT_CORE(24);
SQRT_CORE(22);
SQRT_CORE(20);
SQRT_CORE(18);
SQRT_CORE(16);
SQRT_CORE(14);
SQRT_CORE(12);
SQRT_CORE(10);
SQRT_CORE(8);
SQRT_CORE(6);
SQRT_CORE(4);
SQRT_CORE(2);
SQRT_CORE(0);
#undef SQRT_CORE
return ret;
}
无心人
发表于 2009-2-12 21:30:28
http://www.dianyuan.com/bbs/d/50/143623.html
这个也许有点用处
无心人
发表于 2009-2-12 21:34:53
56#的似乎类似于54#的算法
呵呵
因为不存在乘法和除法
只有逻辑移位和比较
所以比较快
===================
fsqrt的大概58-60个时钟
郭老大的代码最少32个时钟吧
有时间依照54#的逻辑写个汇编
呵呵
liangbch
发表于 2009-2-12 22:28:53
他这个算法是固定15次循环,我想这样做。
1.做一个256 byte 的表。
2. 如果被开放数小于8bit 直接查表得到结果。
否则如果被开方数是k(<8k<=32)bit,则首先通过一次得到最高结果的最高4bit,然后循环 (k-8)/2次循环得到结果的其他bit.
3.程序仍然采用先前的结果,bsr 加跳表。
无心人
发表于 2009-2-13 09:29:53
测试了
FPU2, 54#, 56#代码
2100000000 -- 2200000000
FPU2 Version: 3587.139 ms
iSqrt_16 Version: 26195.432 ms
iSqrt_GxQ Version: 16059.504 ms
页:
1
2
3
4
5
[6]
7
8
9
10
11
12
13
14
15