我正在尝试在没有FPU的体系结构上实现CORDIC算法以近似正弦函数的单精度计算。我将我的实现结果与标准的C数学函数结果进行了比较。我尝试了两种实现方式:1)直接使用浮点运算,2)将输入转换为定点并使用基于整数的运算。我比较了sinf(),sin()和转换为浮点数的sin()的结果。比较基于将结果的十六进制表示与来自数学函数的预期结果进行比较。
在(1)中,实现使用双精度类型,然后将结果转换为浮点数。无论使用CORDIC进行多少次迭代,我的计算值始终至少偏离一个十六进制数字。
在(2)中,最初将输入映射到32位整数。误差与(1)相同。只有在将固定点大小增加到64位(迭代次数增加到64)后,才提高了精度。但是,在某些输入范围内,该算法仍不准确。如果将固定点大小增加到128位(迭代次数增加到128),则可能足以获得准确值,但这完全不切实际。
(1)中的算法是从书籍https://www.jjj.de/fxt/fxtbook.pdf修改而来。
CORDIC表格同样是使用bits=64生成的。
对于(1)的测试如下进行:
在(1)中,实现使用双精度类型,然后将结果转换为浮点数。无论使用CORDIC进行多少次迭代,我的计算值始终至少偏离一个十六进制数字。
在(2)中,最初将输入映射到32位整数。误差与(1)相同。只有在将固定点大小增加到64位(迭代次数增加到64)后,才提高了精度。但是,在某些输入范围内,该算法仍不准确。如果将固定点大小增加到128位(迭代次数增加到128),则可能足以获得准确值,但这完全不切实际。
(1)中的算法是从书籍https://www.jjj.de/fxt/fxtbook.pdf修改而来。
#include <math.h>
#include <stdio.h>
const double cordic_1K = 0.6072529350088812561694467525049282631123908521500897724;
double *cordic_ctab;
void make_cordic_ctab(ulong na)
{
double s = 1.0;
for (ulong k=0; k<na; ++k)
{
cordic_ctab[k] = atan(s);
s *= 0.5;
}
}
void cordic(int theta, double* s, double* c, int n)
{
double x, y, z, v;
double tx, ty, tz;
double d;
x = cordic_1K;
y = 0;
z = theta;
v = 1.0;
for (int k = 0; k < n; ++k) {
d = (z >= 0 ? +1 : -1);
tx = x - d * v * y;
ty = y + d * v * x;
tz = z - d * cordic_ctab[k];
x = tx;
y = ty;
z = tz;
v *= 0.5;
}
*c = x;
*s = y;
}
(2)中的算法是修改版本,可在http://www.dcs.gla.ac.uk/~jhw/cordic/找到。
#include <math.h>
#include <stdio.h>
#define cordic_1K 0x26dd3b6a10d79600
#define CORDIC_NTAB 64
void cordic(long theta, long *s, long *c, int n) {
long d, tx, ty, tz;
long x = cordic_1K, y = 0, z = theta;
n = (n > CORDIC_NTAB) ? CORDIC_NTAB : n;
for (int k = 0; k < n; ++k) {
d = z >= 0 ? 0 : -1;
tx = x - (((y >> k) ^ d) - d);
ty = y + (((x >> k) ^ d) - d);
tz = z - ((cordic_ctab[k] ^ d) - d);
x = tx;
y = ty;
z = tz;
}
*c = x;
*s = y;
}
CORDIC表格同样是使用bits=64生成的。
对于(1)的测试如下进行:
int main(int argc, char **argv) {
float angle;
long s, c;
int failed = 0;
cordic_ctab = (double*)malloc(sizeof(double) * 64);
make_cordic_ctab(64);
for (int i = 0; i < step; i++) {
angle = (i / step) * M_PI / 4;
cordic(angle, &s, &c, 64);
float result = s;
float expected = sinf(angle);
if (angle < pow(2, -27))
result = angle;
if (memcmp(&result, &expected, sizeof(float)) != 0) {
failed += 1;
printf("%e : %e\n", result, expected);
printf("0x%x : 0x%x\n", *((unsigned int *)&result),
*((unsigned int *)&expected));
printf("\n");
}
}
printf("failed:%d\n", failed);
}
对于(2)的测试如下:
int main(int argc, char **argv) {
float angle;
long s, c;
int failed = 0;
double mul = 4611686018427387904.000000;
double step = 1000000000.0;
for (int i = 0; i < step; i++) {
angle = (i / step) * M_PI / 4;
cordic((angle * mul), &s, &c, 64);
float result = s / mul;
float expected = sinf(angle);
if (angle < pow(2, -27))
result = angle;
if (memcmp(&result, &expected, sizeof(float)) != 0) {
failed += 1;
printf("%e : %e\n", result, expected);
printf("0x%x : 0x%x\n", *((unsigned int *)&result),
*((unsigned int *)&expected));
printf("\n");
}
}
printf("failed:%d\n", failed);
}
我是否没有考虑到CORDIC的某些因素?是否有可能CORDIC完全不适用,需要考虑其他方法?