【发布时间】:2021-01-03 05:11:00
【问题描述】:
我目前正在研究余弦的近似值。由于最终目标设备是使用 32 位浮点 ALU / LU 的自行开发,并且有专门的 C 编译器,因此我无法使用 c 库数学函数(cosf,...)。我的目标是编写在准确性和指令/周期数方面不同的各种方法。
我已经尝试了很多不同的逼近算法,从 fdlibm、taylor 展开、pade 逼近、remez 算法使用 maple 等等......
但是,一旦我只使用浮点精度来实现它们,精度就会大大降低。并且可以肯定:我知道使用双精度,更高的精度完全没有问题......
现在,我有一些近似值,精确到 pi/2 附近的几千 ulp(发生最大误差的范围),我觉得我受到单精度转换的限制。
为了解决主题参数减少:输入以弧度为单位。我假设参数减少会由于除法/乘法而导致更多的精度损失......因为我的整体输入范围只有 0..pi,我决定将参数减少到 0..pi/2。
因此我的问题是:有没有人知道高精度的余弦函数的单精度近似(最好的情况是高效率)?是否有任何算法可以优化单精度近似值?你知道内置的 cosf 函数是否在内部以单精度或双精度计算值? ~
float ua_cos_v2(float x)
{
float output;
float myPi = 3.1415927410125732421875f;
if (x < 0) x = -x;
int quad = (int32_t)(x*0.63661977236f);//quad = x/(pi/2) = x*2/pi
if (x<1.58f && x> 1.57f) //exclude approximation around pi/2
{
output = -(x - 1.57079637050628662109375f) - 2.0e-12f*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f) + 0.16666667163372039794921875f*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f) + 2.0e-13f*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)+ 0.000198412701138295233249664306640625f*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f)*(x - 1.57079637050628662109375f);
output -= 4.37E-08f;
}
else {
float param_x;
int param_quad = -1;
switch (quad)
{
case 0:
param_x = x;
break;
case 1:
param_x = myPi - x;
param_quad = 1;
break;
case 2:
param_x = x - myPi;
break;
case 3:
param_x = 2 * myPi - x;
break;
}
float c1 = 1.0f,
c2 = -0.5f,
c3 = 0.0416666679084300994873046875f,
c4 = -0.001388888922519981861114501953125f,
c5 = 0.00002480158218531869351863861083984375f,
c6 = -2.75569362884198199026286602020263671875E-7f,
c7 = 2.08583283978214240050874650478363037109375E-9f,
c8 = -1.10807162057025010426514199934899806976318359375E-11f;
float _x2 = param_x * param_x;
output = c1 + _x2*(c2 + _x2*(c3 + _x2*(c4 + _x2*(c5 + _x2*(c6 + _x2*(c7
+ _x2* c8))))));
if (param_quad == 1 || param_quad == 0)
output = -output;
}
return output;
}
~
如果我忘记了任何信息,请不要犹豫!
提前致谢
【问题讨论】:
-
您需要什么精度?请显示精度不足的近似代码。也许有一些方法可以提高精度。 (不看代码我们无法判断。)请edit你的问题添加此信息,不要使用cmets来回答。
-
您的实际输入是以弧度为单位的,还是您真的想为
0<=x<=1计算cos(x*pi)?无论如何,在应用任何类型的多项式近似之前,您应该将输入范围缩小到[-pi/4, pi/4]并使用诸如cos(x+pi/2) = -sin(x)之类的标识。 -
对于接近 π/2 的 x,也就是你所说的最大误差,cos(x) 在 π/2−x 附近。这意味着用多项式近似它很容易。具体来说,您应该使用 y=π/2−x,然后这种情况下的特定多项式是 y,但即使是某种形式的更一般的多项式,例如 y+c3•y^3+c5•y^5+… 也会有由于高阶项实际上为零,因此计算误差很小。发生错误的地方是计算 y=π/2−x。如果以高精度完成此操作,则 y 的结果在 π/2 附近的 ULP 的一小部分内是准确的。如果使用
float精度完成,则错误很大。 -
对于这种特定情况,您可以考虑将 π/2 分为两部分存储。第一个 p0 是 π/2 舍入到
float。第二个,p1,是 π/2−p0(预先计算,结果写入源代码)。然后 π/2−x 可以在float精度和p0-x+p1中精确计算。当 x 是最接近 π/2 的float时,这会产生大约 ⅓ ULP 的误差。除此之外,我们还需要查看您正在使用的代码。 -
“高精度(最好的情况下是高效率)”——>哪个更重要?我建议最快,只要准确度为
标签: c floating-point trigonometry approximation single-precision