如何快速且高精度地求sin函数?

LyndonZheng 2006-09-14 12:40:14
在一块容量速度都很一般的芯片上运行,浮点数乘法不能超过20次,运算用12位运算,但精度达到12位。上网查过一些方法,速度和精度不能同时达到要求,哪位大哥有好方法介绍一下?谢谢~~
...全文
3727 50 打赏 收藏 转发到动态 举报
写回复
用AI写文章
50 条回复
切换为时间正序
请发表友善的回复…
发表回复
LyndonZheng 2006-09-20
  • 打赏
  • 举报
回复
呵呵,今天搞电了,精度速度空间都可以,结贴了!特别鸣谢疯狂懒猪~亲一下~~~
crazy_lazy_pig 2006-09-19
  • 打赏
  • 举报
回复
呵呵, 我回贴, 有的时候是为了分, 但是楼主却总是不结贴. 而有的时候不是为了分, 却总能得到楼主的贡献.

像这一贴, 我如果不来关心, 就不知道天底下还有个cordic算法, 回了贴, 也学到了东西, 说实在的, 我是应该给人家分的, 哪里还奢求别人给我分啊.
LyndonZheng 2006-09-19
  • 打赏
  • 举报
回复
我发之前那一贴时还未看你的贴,所以才那样问,呵呵~~~

差不多可以结贴了,可惜只有50分,觉得对不起大家:(
crazy_lazy_pig 2006-09-18
  • 打赏
  • 举报
回复
好像楼主还是比较关心我求sinx的方法, 其实, 我是用数学软件算的, 要几个有效数字就能有几个. 我给你的这些数值请放心使用, 足够安全(就是cos(arctan(1/2^50))的值有点奇怪, 不过这并不影响我们的使用).
crazy_lazy_pig 2006-09-18
  • 打赏
  • 举报
回复
以楼主的精度要求, 45次分解足够多了, 下面给出 j=0到50 的arctan(1/2^j)的弧度值(15位有效数字):
[.785398163397448, .463647609000806, .244978663126864,
.124354994546761, .624188099959573e-1, .312398334302683e-1,
.156237286204768e-1, .781234106010111e-2, .390623013196697e-2,
.195312251647882e-2, .976562189559319e-3, .488281211194898e-3,
.244140620149362e-3, .122070311893670e-3, .610351561742088e-4,
.305175781155261e-4, .152587890613158e-4, .762939453110197e-5,
.381469726560650e-5, .190734863281019e-5, .953674316405961e-6,
.476837158203089e-6, .238418579101557e-6, .119209289550780e-6,
.596046447753905e-7, .298023223876953e-7, .149011611938477e-7,
.745058059692383e-8, .372529029846191e-8, .186264514923096e-8,
.931322574615479e-9, .465661287307739e-9, .232830643653870e-9,
.116415321826935e-9, .582076609134674e-10, .291038304567337e-10,
.145519152283669e-10, .727595761418343e-11, .363797880709171e-11,
.181898940354586e-11, .909494701772928e-12, .454747350886464e-12,
.227373675443232e-12, .113686837721616e-12, .568434188608080e-13,
.284217094304040e-13, .142108547152020e-13, .710542735760100e-14,
.355271367880050e-14, .177635683940025e-14, .888178419700125e-15]

以及相对应的角度值:
[45.0000000000000, 26.5650511770779, 14.0362434679265,
7.12501634890177, 3.57633437499734, 1.78991060824607,
.895173710211074, .447614170860553, .223810500368538,
.111905677066207, .559528918938038e-1, .279764526170038e-1,
.139882271422650e-1, .699411367535292e-2, .349705685070401e-2,
.174852842698045e-2, .874264213693784e-3, .437132106872336e-3,
.218566053439348e-3, .109283026720072e-3, .546415133600855e-4,
.273207566800490e-4, .136603783400252e-4, .683018917001269e-5,
.341509458500636e-5, .170754729250319e-5, .853773646251596e-6,
.426886823125798e-6, .213443411562898e-6, .106721705781449e-6,
.533608528907246e-7, .266804264453623e-7, .133402132226812e-7,
.667010661134060e-8, .333505330567029e-8, .166752665283514e-8,
.833763326417575e-9, .416881663208788e-9, .208440831604394e-9,
.104220415802197e-9, .521102079010984e-10, .260551039505492e-10,
.130275519752746e-10, .651377598763729e-11, .325688799381865e-11,
.162844399690932e-11, .814221998454661e-12, .407110999227331e-12,
.203555499613664e-12, .101777749806833e-12, .508888749034163e-13]

还有其cos值:
[.707106781186550, .894427190999916, .970142500145332,
.992277876713667, .998052578482889, .999512076087081,
.999877952034693, .999969483818786, .999992370692778,
.999998092656822, .999999523163180, .999999880790732,
.999999970197680, .999999992549423, .999999998137356,
.999999999534336, .999999999883586, .999999999970898,
.999999999992723, .999999999998181, .999999999999549,
.999999999999888, .999999999999972, .999999999999997,
.999999999999996, .999999999999996, 1.00000000000000,
1.00000000000000, .999999999999999, 1.00000000000000,
1.00000000000000, .999999999999999, 1.00000000000000,
1.00000000000000, .999999999999996, 1.00000000000000,
1.00000000000000, 1.00000000000000, .999999999999999,
.999999999999999, 1.00000000000000, 1.00000000000000,
1.00000000000000, 1.00000000000000, 1.00000000000000,
1.00000000000000, 1.00000000000000, 1.00000000000000,
1.00000000000000, 1.00000000000000, .999999999999996]
crazy_lazy_pig 2006-09-18
  • 打赏
  • 举报
回复
cordic算法其实跟我的算法差不多. 也是把角度分解, 但是分解的方法跟我不一样, 它是用一种类似于二分法的逼近思路, 有可能分解的比我多, 但是分解后就不需要乘法计算了. 具体分解法如下:

仍然假设角theta位于0到90度之间, 首先我们把直角平分, 得到一个45度的角( tan45=1=2^0 ), 那么alfa要么在平分线上方, 要么就在下方, 若在上方, 则作从平分线开始向上作一个角, 使得其tan=1/2=2^(-1), 否则向下做该角, 这样角theta必在新作出来的线上或者线下, 继续如上法作角, 使tan=1/4=2^(-2), 如此一直做下去, 我们就得到一组线, 该线的极限就是 theta 角. 这样我们就得到了 theta的一个分解(有可能是无穷的):
theta=alfa0 + delta1*alfa1 + delta2*alfa2 + ... + deltaj * alfaj +... ,
其中tan(alfaj)=2^(-j) , delta=-1 或者 +1, 在向上作角时其值为正1 , 向下时为 -1 .

和差化积公式:
sin( x + alfaj ) = cos(x)*sin(alfaj) + sin(x)*cos(alfaj)
= ( sin(x) + cos(x)*tan(alfaj) ) * cos(alfaj)
cos( x + alfaj ) = cos(x)*cos(alfaj) + sin(x)*sin(alfaj)
= ( cos(x) - sin(x)*tan(alfaj) ) * cos(alfaj)
现令:
s[j+1] = s[j] + c[j] * tan(deltaj*alfaj) = s[j] + deltaj*c[j]*(2^(-j)) = s[j]+deltaj*c[j]>>j
c[j+1] = c[j] - s[j] * tan(deltaj*alfaj) = c[j] - deltaj*s[j]*(2^(-j)) = c[j]-deltaj*s[j]>>j
这其中alfaj为上面我们谈到的theta的分解项, s[j],c[j]分别类似于sin( alfa0 + ... + alfaj ),cos( alfa0 + ... + alfaj ), 但是, 对照上面的和差化积公式, 我们应该发现, 在每次转化的时候, 我们都少乘了一个系数:cos(alfaj). 因此sqrt(s[j+1]*s[j+1] + c[j+1]*c[j+1]) = sqrt(s[j]*s[j] + c[j]*c[j])/cos(alfaj), 即
s[j]*(cos(alfa0*...*cos(alfaj-1))=sin(alfa0+...+alfaj)
这样我们通过若干次移位运算和加法(减法), 就可以得到sin(theta)的一个放大了的值, 再除以一个放大系数cos(alfa0*...*cos(alfaj-1)就可以了(这里似乎是要乘法运算了).

下面的关键就是求theta的分解式了, 即确定delta的值(注意, alfaj的值是确定的). 为此我们需要事先建立一个表: alfa[N] 来存放alfaj的值, 使其满足: tan( alfa[j] ) = 2^(-j),且tan(alfa[N-1])<e (e为你所需的精度). 我们令 t[0] = theta, delta[0]=1.
t[j+1]=t[j]-alfa[j]*delta[j], 若t[j+1]<0, 则delta[j+1]=-1, 否则delta[j+1]=1
另外cos(alfaj)可以由tan(alfaj)=2^(-1) 求得, 如果这里仍然不想作乘法, 那么索性再做个表.

最后总结一下求解过程:
while( t[j+1]=t[j]-alfa[j]*delta[j] == 0 ){
delta[j+1]=sgn(t[j+1]);
s[j+1] = s[j]+delta[j]*c[j]>>j;
c[j+1] = c[j]-delta[j]*s[j]>>j;
j++
}
s[j]*=cos(alfa0)*cos(alfa1)*...*cos(alfaj);//注意这是伪代码或许有错
crazy_lazy_pig 2006-09-18
  • 打赏
  • 举报
回复
对了, 忘了精度问题了, 又计算了一下, 在j>20时, tan(1/2^j)=1/2^j, 所以第一个表存贮20个值就足够了.
这样加上那些cos大概需要四十多个存贮空间, 可以把我原来算法的空间占用率减半, 不知道还大不大了?
crazy_lazy_pig 2006-09-18
  • 打赏
  • 举报
回复
计算器是怎么做出来的?
=========================
这个问题我也不知道, 得问计算的设计者

说误差是1/(2的-12次方)?
=========================
不知道楼主看的是什么资料, 我不认为它是这么精度, 精度完全是人定的, 一个算法怎么能有确定的精度呢? 如果有, 那么它肯定是不好的. 也许你看的算法说的这个精度是在最后一步的取值上, 我看的一个材料中是直接将其取成0.678(记不清楚了, 反正是个固定值)而不是若干个cos的乘积, 这样做当然是误差比较大的, 但是, 即使是这样, 它仍然能保证精度在1/(2的-12次方), 可见其算法的高精度.
不知道楼主是否看懂了我的回贴, 我贴出的算法是可以精确到任意精度的, 按照我给你的数字计算的话, 则可以达到15位有效数字, 且最多花费80个固定存贮单元.
LyndonZheng 2006-09-18
  • 打赏
  • 举报
回复
看了一下cordic算法的资料,说误差是1/(2的-12次方),是否属实?如果真的是这样的话这个算法还是不可取哦~~~
AFIC 2006-09-18
  • 打赏
  • 举报
回复
MARK
caoze 2006-09-18
  • 打赏
  • 举报
回复

计算器是怎么做出来的?
LyndonZheng 2006-09-17
  • 打赏
  • 举报
回复
回Polaris_()
我对你的算法非常之感兴趣.感谢你的指教~~我的qq是723956048 名称叫san。
油箱是san_77227487@163.com
waiting for you~~Thank you!

另外有一点可能忘了说,向大家说声不好意思先:(
就是乘法是用13位十进制数(13个nipple)进行相乘然后取12位的,一些多数位设备可以运算的算法在这里应该不可用,比如说tailor级数。具体怎样不可用我也说不清,是boss说他们以前用过tailor,但失败了。
LyndonZheng 2006-09-17
  • 打赏
  • 举报
回复
回疯狂的猪
你的方法确实可行,而且我现在正在完善中。不过我的方法有点不同,我是用一种逼近法求出0到1度的sin值,再用另一种逼近方法求出cos值。为什么要求出cos呢?因为角相加需要用到。不知道你是用什么办法可以通过x求出sinx并精确到12位呢?我的逼近法用了8次乘法,再用6次乘法求出cos。另,把1度至90度的sin值和cos值都保存进去需要0.6k个nipple,我老板说太大了。:< 你有没好的对策?等待你的回复,谢谢~~我的邮箱是san_77227487@163.com
crystalshark 2006-09-17
  • 打赏
  • 举报
回复
可以考虑用硬件搭
否则,我想用空间换时间的方案应该可行
kmlxk0 2006-09-17
  • 打赏
  • 举报
回复
仰望一下
llmice 2006-09-17
  • 打赏
  • 举报
回复
复杂,高级,顶。。。。
lifehot 2006-09-17
  • 打赏
  • 举报
回复
up...........................
Joyfish 2006-09-17
  • 打赏
  • 举报
回复
mark
LyndonZheng 2006-09-17
  • 打赏
  • 举报
回复
cordic algorithm的确是个好办法,研究中。。。。
已经懂了的达人可以指教一下吗?不胜感激!
hawk234 2006-09-16
  • 打赏
  • 举报
回复
up
加载更多回复(28)

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧