详解QT中的快速正弦、快速余弦函数的实现

一、问题描述

  数值计算中,许多数学函数(如正弦、余弦、平方根等)可以通过泰勒级数展开的方法求得,然而,对于正弦函数与余弦函数,采用泰勒级数展开的方法收敛速度慢,计算速度慢。那么,有没有快速计算正弦函数与余弦函数并且可以估算误差限的算法呢?
  QT中提供了快速正弦函数、快速余弦函数的算法,代码片段如下:

#define QT_SINE_TABLE_SIZE 256
#define M_PI (3.14159265358979323846)
inline qreal qFastSin(qreal x)
{
    int si = int(x * (0.5 * QT_SINE_TABLE_SIZE / M_PI)); // Would be more accurate with qRound, but slower.
    qreal d = x - si * (2.0 * M_PI / QT_SINE_TABLE_SIZE);
    int ci = si + QT_SINE_TABLE_SIZE / 4;
    si &= QT_SINE_TABLE_SIZE - 1;
    ci &= QT_SINE_TABLE_SIZE - 1;
    return qt_sine_table[si] + (qt_sine_table[ci] - 0.5 * qt_sine_table[si] * d) * d;
}

inline qreal qFastCos(qreal x)
{
    int ci = int(x * (0.5 * QT_SINE_TABLE_SIZE / M_PI)); // Would be more accurate with qRound, but slower.
    qreal d = x - ci * (2.0 * M_PI / QT_SINE_TABLE_SIZE);
    int si = ci + QT_SINE_TABLE_SIZE / 4;
    si &= QT_SINE_TABLE_SIZE - 1;
    ci &= QT_SINE_TABLE_SIZE - 1;
    return qt_sine_table[si] - (qt_sine_table[ci] + 0.5 * qt_sine_table[si] * d) * d;
}

二、推导过程

  QT中提供的快速正弦、快速余弦的算法,是一种典型的以空间换时间查表法。这里以快速正弦函数为例进行讲解,对于快速余弦函数很容易类比推导得到。
  由于 s i n ( x ) sin(x) sin(x)为周期为 2 π 2\pi 2π的函数,对于任意实数 x x x均可以变换到区间 [ 0 , 2 π ] [0,2\pi] [0,2π],假设区间 [ 0 , 2 π ] [0,2\pi] [0,2π]的正弦表 s i n e T a b l e sineTable sineTable长度为 N N N
  求 s i n ( x ) sin(x) sin(x)时,实数 x x x所对应的最接近其值的正弦表索引(不一定在 0 至 N − 1 0 至 N-1 0N1之间):
s i = r o u n d ( x N 2 π ) (1) s_i=round(\dfrac{xN}{2\pi})\tag 1 si=round(2πxN)(1)
  由于调用 r o u n d round round取整函数增加了函数调用的开销,这里用其他方式实现 r o u n d round round函数的功能。若 x > 0 x>0 x>0,则 s i g n = 1 sign=1 sign=1;否则, s i g n = − 1 sign=-1 sign=1。计算 r o u n d ( x N 2 π ) round(\dfrac{xN}{2\pi}) round(2πxN)等价于计算 ( i n t ) ( x N 2 π + s i g n ∗ 0.5 ) (int) (\dfrac{xN}{2\pi}+sign*0.5) (int)(2πxN+sign0.5)

  计算 x x x与所对应的最接近其值的正弦表横坐标 x s i x_{s_i} xsi的偏差:
d = x − s i 2 π N (2) d=x-s_i \dfrac{2\pi}{N} \tag 2 d=xsiN2π(2)
  由于 c o s ( x ) = s i n ( x + π / 2 ) cos(x)=sin(x+\pi/2) cos(x)=sin(x+π/2),因而,求 c o s ( x ) cos(x) cos(x)时,实数 x x x所对应的最接近其值的正弦表索引(不一定在 0 至 N − 1 0 至 N-1 0N1之间):
c i = s i + N / 4 (3) c_i=s_i + N/4\tag 3 ci=si+N/4(3)
  由于除法运算较慢,上式可以采用移位运算代替: c i = s i + ( N > > 2 ) c_i=s_i +( N>>2) ci=si+(N>>2)
  将 s i , c i s_i,c_i si,ci变换到 0 至 N − 1 0至N-1 0N1之间:
{ s i = s i & ( N − 1 ) c i = c i & ( N − 1 ) (4) \begin{cases} s_i=s_i \& (N-1) \\ c_i=c_i \& (N-1) \\ \tag 4 \end{cases} {si=si&(N1)ci=ci&(N1)(4)
  其中, & \& &表示按位与。
  由于 s i n ( x ) = s i n ( x s i + d ) = s i n ( x s i ) c o s ( d ) + c o s ( x s i ) s i n ( d ) sin(x)=sin(x_{s_i}+d)=sin(x_{s_i})cos(d)+cos(x_{s_i})sin(d) sin(x)=sin(xsi+d)=sin(xsi)cos(d)+cos(xsi)sin(d) d d d很小, s i n ( d ) , c o s ( d ) sin(d),cos(d) sin(d),cos(d)用等价无穷小代替:
{ s i n ( d ) ∼ d c o s ( d ) ∼ 1 − d 2 / 2 (5) \begin{cases} sin(d) \sim d \\ cos(d)\sim1-d^2/2 \\ \tag 5 \end{cases} {sin(d)dcos(d)1d2/2(5)
  得到:
s i n ( x ) = s i n e T a b l e [ s i ] + ( s i n e T a b l e [ c i ] − 0.5 ∗ s i n e T a b l e [ s i ] ∗ d ) ∗ d (6) sin(x)=sineTable[s_i] +(sineTable[c_i] - 0.5*sineTable[s_i]*d) *d \tag 6 sin(x)=sineTable[si]+(sineTable[ci]0.5sineTable[si]d)d(6)
  特别的,当需要同时计算 s i n ( x ) , c o s ( x ) sin(x),cos(x) sin(x),cos(x)时,为了进一步减小计算量,避免重复计算 d , s s , c i d,s_s,c_i d,ss,ci,可以将计算 s i n ( x ) , c o s ( x ) sin(x),cos(x) sin(x),cos(x)写成一个函数。
   s i n ( x ) sin(x) sin(x) 0 0 0处的泰勒展开为: s i n ( x ) = x − x 3 / 6 + x 5 / 120 − x 7 / 5040 + . . . sin(x)=x-x^3/6 + x^5/120- x^7/5040+... sin(x)=xx3/6+x5/120x7/5040+...
   c o s ( x ) cos(x) cos(x) 0 0 0处的泰勒展开为: c o s ( x ) = 1 − x 2 / 2 + x 4 / 24 − x 6 / 720 + . . . cos(x)=1- x^2/2 + x^4/24- x^6/720+... cos(x)=1x2/2+x4/24x6/720+...
  采用式(5)的等价无穷小表示时, s i n ( d ) sin(d) sin(d)的误差限约为 ϵ s = d m a x 3 / 6 \epsilon_{s}=d_{max}^3/6 ϵs=dmax3/6, c o s ( d ) cos(d) cos(d)的误差限约为 ϵ c = d m a x 4 / 24 \epsilon_{c}=d_{max}^4/24 ϵc=dmax4/24, d m a x = π / ( N − 1 ) d_{max}=\pi/(N-1) dmax=π/(N1)
  计算 s i n ( x ) sin(x) sin(x)的误差限:
ϵ s i n e ≤ ∣ s i n ( x s i ) ∣ m a x ∗ ϵ c + ∣ c o s ( x s i ) ∣ m a x ∗ ϵ s = ϵ c + ϵ s (7) \epsilon_{sine}\le |sin(x_{s_i})|_{max} * \epsilon_{c} + |cos(x_{s_i})|_{max} * \epsilon_{s}=\epsilon_{c}+\epsilon_{s} \tag 7 ϵsinesin(xsi)maxϵc+cos(xsi)maxϵs=ϵc+ϵs(7)

  同理可计算 c o s ( x ) cos(x) cos(x)的误差限:
ϵ c o s i n e ≤ ϵ c + ϵ s (8) \epsilon_{cosine}\le \epsilon_{c}+\epsilon_{s} \tag 8 ϵcosineϵc+ϵs(8)
  

三、c代码

  快速正弦函数、快速余弦函数的c语言实现如下:

#define SINE_TABLE_SIZE 256
static const double sineTable[SINE_TABLE_SIZE] = {
	0.0,
	0.024541228522912288,
	0.049067674327418015,
	0.073564563599667426,
	0.098017140329560604,
	0.1224106751992162,
	0.14673047445536175,
	0.17096188876030122,
	0.19509032201612825,
	0.2191012401568698,
	0.24298017990326387,
	0.26671275747489837,
	0.29028467725446233,
	0.31368174039889152,
	0.33688985339222005,
	0.35989503653498811,
	0.38268343236508978,
	0.40524131400498986,
	0.42755509343028208,
	0.44961132965460654,
	0.47139673682599764,
	0.49289819222978404,
	0.51410274419322166,
	0.53499761988709715,
	0.55557023301960218,
	0.57580819141784534,
	0.59569930449243336,
	0.61523159058062682,
	0.63439328416364549,
	0.65317284295377676,
	0.67155895484701833,
	0.68954054473706683,
	0.70710678118654746,
	0.72424708295146689,
	0.74095112535495911,
	0.75720884650648446,
	0.77301045336273699,
	0.78834642762660623,
	0.80320753148064483,
	0.81758481315158371,
	0.83146961230254524,
	0.84485356524970701,
	0.85772861000027212,
	0.87008699110871135,
	0.88192126434835494,
	0.89322430119551532,
	0.90398929312344334,
	0.91420975570353069,
	0.92387953251128674,
	0.93299279883473885,
	0.94154406518302081,
	0.94952818059303667,
	0.95694033573220894,
	0.96377606579543984,
	0.97003125319454397,
	0.97570213003852857,
	0.98078528040323043,
	0.98527764238894122,
	0.98917650996478101,
	0.99247953459870997,
	0.99518472667219682,
	0.99729045667869021,
	0.99879545620517241,
	0.99969881869620425,
	1.0,
	0.99969881869620425,
	0.99879545620517241,
	0.99729045667869021,
	0.99518472667219693,
	0.99247953459870997,
	0.98917650996478101,
	0.98527764238894122,
	0.98078528040323043,
	0.97570213003852857,
	0.97003125319454397,
	0.96377606579543984,
	0.95694033573220894,
	0.94952818059303667,
	0.94154406518302081,
	0.93299279883473885,
	0.92387953251128674,
	0.91420975570353069,
	0.90398929312344345,
	0.89322430119551521,
	0.88192126434835505,
	0.87008699110871146,
	0.85772861000027212,
	0.84485356524970723,
	0.83146961230254546,
	0.81758481315158371,
	0.80320753148064494,
	0.78834642762660634,
	0.7730104533627371,
	0.75720884650648468,
	0.74095112535495899,
	0.72424708295146689,
	0.70710678118654757,
	0.68954054473706705,
	0.67155895484701855,
	0.65317284295377664,
	0.63439328416364549,
	0.61523159058062693,
	0.59569930449243347,
	0.57580819141784545,
	0.55557023301960218,
	0.53499761988709715,
	0.51410274419322177,
	0.49289819222978415,
	0.47139673682599786,
	0.44961132965460687,
	0.42755509343028203,
	0.40524131400498992,
	0.38268343236508989,
	0.35989503653498833,
	0.33688985339222033,
	0.31368174039889141,
	0.29028467725446239,
	0.26671275747489848,
	0.24298017990326407,
	0.21910124015687005,
	0.19509032201612861,
	0.17096188876030122,
	0.1467304744553618,
	0.12241067519921635,
	0.098017140329560826,
	0.073564563599667732,
	0.049067674327417966,
	0.024541228522912326,
	0.0,
	-0.02454122852291208,
	-0.049067674327417724,
	-0.073564563599667496,
	-0.09801714032956059,
	-0.1224106751992161,
	-0.14673047445536158,
	-0.17096188876030097,
	-0.19509032201612836,
	-0.2191012401568698,
	-0.24298017990326382,
	-0.26671275747489825,
	-0.29028467725446211,
	-0.31368174039889118,
	-0.33688985339222011,
	-0.35989503653498811,
	-0.38268343236508967,
	-0.40524131400498969,
	-0.42755509343028181,
	-0.44961132965460665,
	-0.47139673682599764,
	-0.49289819222978393,
	-0.51410274419322155,
	-0.53499761988709693,
	-0.55557023301960196,
	-0.57580819141784534,
	-0.59569930449243325,
	-0.61523159058062671,
	-0.63439328416364527,
	-0.65317284295377653,
	-0.67155895484701844,
	-0.68954054473706683,
	-0.70710678118654746,
	-0.72424708295146678,
	-0.74095112535495888,
	-0.75720884650648423,
	-0.77301045336273666,
	-0.78834642762660589,
	-0.80320753148064505,
	-0.81758481315158382,
	-0.83146961230254524,
	-0.84485356524970701,
	-0.85772861000027201,
	-0.87008699110871135,
	-0.88192126434835494,
	-0.89322430119551521,
	-0.90398929312344312,
	-0.91420975570353047,
	-0.92387953251128652,
	-0.93299279883473896,
	-0.94154406518302081,
	-0.94952818059303667,
	-0.95694033573220882,
	-0.96377606579543984,
	-0.97003125319454397,
	-0.97570213003852846,
	-0.98078528040323032,
	-0.98527764238894111,
	-0.9891765099647809,
	-0.99247953459871008,
	-0.99518472667219693,
	-0.99729045667869021,
	-0.99879545620517241,
	-0.99969881869620425,
	-1.0,
	-0.99969881869620425,
	-0.99879545620517241,
	-0.99729045667869021,
	-0.99518472667219693,
	-0.99247953459871008,
	-0.9891765099647809,
	-0.98527764238894122,
	-0.98078528040323043,
	-0.97570213003852857,
	-0.97003125319454397,
	-0.96377606579543995,
	-0.95694033573220894,
	-0.94952818059303679,
	-0.94154406518302092,
	-0.93299279883473907,
	-0.92387953251128663,
	-0.91420975570353058,
	-0.90398929312344334,
	-0.89322430119551532,
	-0.88192126434835505,
	-0.87008699110871146,
	-0.85772861000027223,
	-0.84485356524970723,
	-0.83146961230254546,
	-0.81758481315158404,
	-0.80320753148064528,
	-0.78834642762660612,
	-0.77301045336273688,
	-0.75720884650648457,
	-0.74095112535495911,
	-0.724247082951467,
	-0.70710678118654768,
	-0.68954054473706716,
	-0.67155895484701866,
	-0.65317284295377709,
	-0.63439328416364593,
	-0.61523159058062737,
	-0.59569930449243325,
	-0.57580819141784523,
	-0.55557023301960218,
	-0.53499761988709726,
	-0.51410274419322188,
	-0.49289819222978426,
	-0.47139673682599792,
	-0.44961132965460698,
	-0.42755509343028253,
	-0.40524131400499042,
	-0.38268343236509039,
	-0.359895036534988,
	-0.33688985339222,
	-0.31368174039889152,
	-0.2902846772544625,
	-0.26671275747489859,
	-0.24298017990326418,
	-0.21910124015687016,
	-0.19509032201612872,
	-0.17096188876030177,
	-0.14673047445536239,
	-0.12241067519921603,
	-0.098017140329560506,
	-0.073564563599667412,
	-0.049067674327418091,
	-0.024541228522912448
};
/**********************************************************************************************
Function: fast_sin
Description: 快速正弦函数
Input: 浮点数x
Output: 无
Input_Output: 无
Return: sin(x)
Author: Marc Pony(marc_pony@163.com)
***********************************************************************************************/
double fast_sin(double x)
{
	int sign = x > 0.0 ? 1 : -1;
	int si = (int)(x * SINE_TABLE_SIZE / (2.0 * PI) + sign * 0.5); //+0.5后取整等价于round函数
	double d = x - si * (2.0 * PI / SINE_TABLE_SIZE);
	int ci = si + (SINE_TABLE_SIZE >> 2);
	si &= (SINE_TABLE_SIZE - 1);
	ci &= (SINE_TABLE_SIZE - 1);
	return sineTable[si] + (sineTable[ci] - 0.5 * sineTable[si] * d) * d;
}


/**********************************************************************************************
Function: fast_cos
Description: 快速余弦函数
Input: 浮点数x
Output: 无
Input_Output: 无
Return: cos(x)
Author: Marc Pony(marc_pony@163.com)
***********************************************************************************************/
double fast_cos(double x)
{
	int sign = x > 0.0 ? 1 : -1;
	int ci = (int)(x * SINE_TABLE_SIZE / (2.0 * PI) + sign * 0.5); //+0.5后取整等价于round函数
	double d = x - ci * (2.0 * PI / SINE_TABLE_SIZE);
	int si = ci + (SINE_TABLE_SIZE >> 2);
	si &= (SINE_TABLE_SIZE - 1);
	ci &= (SINE_TABLE_SIZE - 1);
	return sineTable[si] - (sineTable[ci] + 0.5 * sineTable[si] * d) * d;
}


/**********************************************************************************************
Function: fast_sin_cos
Description: 快速正弦函数和快速余弦函数同时计算
Input: 浮点数x
Output: 正弦值sinX,余弦值cosX
Input_Output: 无
Return: 无
Author: Marc Pony(marc_pony@163.com)
***********************************************************************************************/
void fast_sin_cos(double x, double* sinX, double* cosX)
{
	int sign = x > 0.0 ? 1 : -1;
	int si = (int)(x * SINE_TABLE_SIZE / (2.0 * PI) + sign * 0.5);  //+0.5后取整等价于round函数
	double d = x - si * (2.0 * PI / SINE_TABLE_SIZE);
	int ci = si + (SINE_TABLE_SIZE >> 2);
	si &= (SINE_TABLE_SIZE - 1);
	ci &= (SINE_TABLE_SIZE - 1);

	*sinX = sineTable[si] + (sineTable[ci] - 0.5 * sineTable[si] * d) * d;
	*cosX = sineTable[ci] - (sineTable[si] + 0.5 * sineTable[ci] * d) * d;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <time.h>
#include <math.h>
#define TEST_COUNT 10000000
int main()
{
	int i, N[3];
	long startTime, finishTime;
	double deltaX, maxErrorSine, maxErrorCosine;
	double dmax, epsilon, x, s1, c1, s2, c2;
	
	startTime = clock();
	for (i = 0; i < TEST_COUNT; i++)
	{
		s1 = sin(i);
		c1 = cos(i);
	}
	finishTime = clock();
	printf("优化前计算时间: %f s\n", (finishTime - startTime) / 1000.0);

	startTime = clock();
	for (i = 0; i < TEST_COUNT; i++)
	{
		fast_sin_cos(i, &s2, &c2);
	}
	finishTime = clock();
	printf("优化后计算时间: %f s\n", (finishTime - startTime) / 1000.0);

	maxErrorSine = 0.0;
	maxErrorCosine = 0.0;
	deltaX = 2.0 * PI / (TEST_COUNT - 1);
	for (i = 0; i < TEST_COUNT; i++)
	{
		x = i * deltaX;
		s1 = sin(x);
		c1 = cos(x);
		fast_sin_cos(x, &s2, &c2);

		if (fabs(s1 - s2) > maxErrorSine)
		{
			maxErrorSine = fabs(s1 - s2);
		}
		if (fabs(c1 - c2) > maxErrorCosine)
		{
			maxErrorCosine = fabs(c1 - c2);
		}
	}
	printf("快速正弦函数最大计算误差: %e\n", maxErrorSine);
	printf("快速余弦函数最大计算误差: %e\n", maxErrorCosine);

	N[0] = 256;
	N[1] = 512;
	N[2] = 1024;
	for (i = 0; i < 3; i++)
	{
		dmax = PI / (N[i] - 1);
		epsilon = dmax * dmax * dmax / 6.0 + dmax * dmax * dmax * dmax / 24.0;
		printf("当N=%d时,快速正弦函数与快速余弦函数理论误差限: %e\n", N[i], epsilon);
	}

	getchar();

	return 0;
}

在这里插入图片描述

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值