【发布时间】:2014-11-29 19:38:56
【问题描述】:
基本上,我一直在尝试制作两个近似函数。在这两种情况下,我都输入了“x”和“y”组件(以处理那些讨厌的 n/0 和 0/0 条件),并且需要得到一个 Signed Char 输出。在 ATAN2 的情况下,它应该提供 +/-PI 的范围,在 ATAN 的情况下,范围应该是 +/- PI/2。
我昨天整个时间都在试图绕过它。在 excel 中玩耍后,根据近似值找到了一个整体良好的算法:
X * (PI/4 + 0.273 * (1 - |X|)) * 128/PI // Scale factor at end to switch to char format
我想出了以下代码:
signed char nabsSC(signed char x)
{
if(x > 0)
return -x;
return x;
}
signed char signSC(signed char input, signed char ifZero = 0, signed char scaleFactor = 1)
{
if(input > 0)
{return scaleFactor;}
else if(input < 0)
{return -scaleFactor;}
else
{return ifZero;}
}
signed char divisionSC(signed char numerator, signed char denominator)
{
if(denominator == 0) // Error Condition
{return 0;}
else
{return numerator/denominator;}
}
//#######################################################################################
signed char atan2SC(signed char y, signed char x)
{
// @todo make clearer : the code was deduced through trial and error in excel with brute force... not the best reasoning in the world but hey ho
if((x == y) && (x == 0)) // Error Condition
{return 0;}
// Prepare for algorithm Choice
const signed char X = abs(x);
signed char Y = abs(y);
if(Y > 2)
{Y = (Y << 1) + 4;}
const signed char alpha1 = 43;
const signed char alpha2 = 11;
// Make Choice
if(X <= Y) // x/y Path
{
const signed char beta = 64;
const signed char a = divisionSC(x,y); // x/y
const signed char A = nabsSC(a); // -|x/y|
const signed char temp = a * (alpha1 + alpha2 * A); // (x/y) * (32 + ((0.273 * 128) / PI) * (1 - |x/y|)))
// Small angle approximation of ARCTAN(X)
if(y < 0) // Determine Quadrant
{return -(temp + beta);}
else
{return -(temp - beta);}
}
else // y/x Path
{
const signed char a = divisionSC(y,x); // y/x
const signed char A = nabsSC(a); // -|y/x|
const signed char temp = a * (alpha1 + alpha2 * A); // (y/x) * (32 + ((0.273 * 128) / PI) * (1 - |y/x|)))
// Small angle approximation of ARCTAN(X)
if(x < 0) // Determine Quadrant
{
Y = signSC(y, -127, 127); // Sign(y)*127, if undefined: use -127
return temp + Y;
}
else
{return temp;}
}
}
令我非常失望的是,实现的错误高达 180 度,而且几乎无处不在。 (我在转换为有符号字符格式后将其与库中的 ATAN2F 进行了比较。)
我从这个网站得到了一般要点:http://geekshavefeelings.com/posts/fixed-point-atan2
谁能告诉我哪里出错了?以及我应该如何在没有这些疯狂的情况下接近 ATAN 变体(应该更精确,因为它看起来超过一半的范围)。
我目前在 Windows 上使用 QT creator 4.8.1。这段特定代码的最终平台最终将是一个没有 FPU 的微控制器,ATAN 功能将是使用的主要功能之一。因此,具有合理误差的效率(ATAN2 为 +/-2 度,ATAN 为 +/-1 度。这些是目前的猜测,所以我可能会增加范围,但是,90 度绝对是不可接受的!)是目标游戏。
提前感谢您的任何帮助!
编辑: 澄清一下,ATAN2 和 ATAN 的输出是有符号的 char 值,但是这两种类型的范围是不同的范围。
ATAN2 的范围应为 -128 (-PI) 到 127 (+PI - PI/128)。
ATAN 的范围为 -128 (-PI/2) 到 127 (+PI/2 - PI/256)。
因此,两者的输出值可以被认为是两种不同的数据类型。
如有任何混淆,请见谅。
EDIT2:将隐式 int 数字显式转换为带符号的 char 常量。
【问题讨论】:
-
请注意,一个 8 位字节(如果您的微控制器上的
CHAR_BIT= 8)没有超过 256 个可能的值,而一个圆有 360 度。也许你可以写一些你想要的角度测量单位。 -
代码在
signed char上使用了大量操作,显然代码不应使用浮点数。但在C中,temp = a * (43 + 11 * A);调用int操作,而不仅仅是signed char操作。 1)int操作是否可以接受? 2) 你的微控制器的int大小是多少。 3)你考虑过CORDIC吗? -
一些参考信息BAMs.
-
您有 256 个可能的值映射到 256 个值。为什么不直接“离线”计算它,并在代码中使用查找表(这只会“花费”你 256 字节的内存)?
-
@chux 感谢您对隐式 int 声明的提醒。我希望现在已经明确定义了它们。在这种情况下,不能接受 int 操作。我查看了 CORDIC,但它在不使用多次迭代的情况下的性能可靠性困扰了我。感谢 BAM 的链接,我已经掌握了一般要点,但没有给它命名。