Iphone 在计算角度时避免atan2-atan2精度

Iphone 在计算角度时避免atan2-atan2精度,iphone,objective-c,c,math,geometry,Iphone,Objective C,C,Math,Geometry,我们,开发人员经常需要计算角度来执行旋转。通常我们可以使用atan2()函数,但有时我们需要更高的精度。那你怎么办 我知道理论上atan2是精确的,但在我的系统(iOS)中,它的精度约为0.05弧度,所以差别很大。这不仅仅是我的问题。我也看到过类似的观点。如果系统中的长双精度比双精度更精确,则可以使用atan2l long double atan2l(long double y, long double x); atan2用于从向量(x,y)获取角度a。如果使用此角度应用旋转,则将使用cos(

我们,开发人员经常需要计算角度来执行旋转。通常我们可以使用atan2()函数,但有时我们需要更高的精度。那你怎么办


我知道理论上atan2是精确的,但在我的系统(iOS)中,它的精度约为0.05弧度,所以差别很大。这不仅仅是我的问题。我也看到过类似的观点。

如果系统中的
长双精度
双精度
更精确,则可以使用
atan2l

long double atan2l(long double y, long double x);

atan2
用于从向量
(x,y)
获取角度
a
。如果使用此角度应用旋转,则将使用
cos(a)
sin(a)
。您可以通过归一化(x,y)简单地计算cos和sin,并保留它们而不是角度。精度将更高,并且可以节省在三角函数中丢失的大量周期


编辑。如果你真的想要一个从(x,y)开始的角度,可以使用的变量计算出你需要的精度。

在iOS上,我发现标准的三角运算符精确到大约13或14位小数,所以你看到0.05弧度的误差听起来很奇怪。如果您可以生成代码和特定值来演示这一点,请在行为上进行说明(并在此处发布代码,以便我们可以对其进行记录)

也就是说,如果您真的需要三角运算符的高精度,我已经修改了Dave DeLong为其代码创建的一些例程。这些例程使用NSDecimal执行数学运算,为您提供多达34位的十进制精度,同时避免了以10位小数为基数表示的标准浮点问题。您可以从下载这些修改例程的代码

使用以下代码计算NSDecimal版本的
atan()

NSDecimal DDDecimalAtan(NSDecimal x) {
    // from: http://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Infinite_series

    // The normal infinite series diverges if x > 1
    NSDecimal one = DDDecimalOne();
    NSDecimal absX = DDDecimalAbsoluteValue(x);

    NSDecimal z = x;
    if (NSDecimalCompare(&one, &absX) == NSOrderedAscending) 
    {
        // y = x / (1 + sqrt(1+x^2))
        // Atan(x) = 2*Atan(y)
        // From: http://www.mathkb.com/Uwe/Forum.aspx/math/14680/faster-Taylor-s-series-of-Atan-x

        NSDecimal interiorOfRoot;
        NSDecimalMultiply(&interiorOfRoot, &x, &x, NSRoundBankers);
        NSDecimalAdd(&interiorOfRoot, &one, &interiorOfRoot, NSRoundBankers);
        NSDecimal denominator = DDDecimalSqrt(interiorOfRoot);
        NSDecimalAdd(&denominator, &one, &denominator, NSRoundBankers);
        NSDecimal y;
        NSDecimalDivide(&y, &x, &denominator, NSRoundBankers);

        NSDecimalMultiply(&interiorOfRoot, &y, &y, NSRoundBankers);
        NSDecimalAdd(&interiorOfRoot, &one, &interiorOfRoot, NSRoundBankers);
        denominator = DDDecimalSqrt(interiorOfRoot);
        NSDecimalAdd(&denominator, &one, &denominator, NSRoundBankers);
        NSDecimal y2;
        NSDecimalDivide(&y2, &y, &denominator, NSRoundBankers);

//        NSDecimal two = DDDecimalTwo();
        NSDecimal four = DDDecimalFromInteger(4);
        NSDecimal firstArctangent = DDDecimalAtan(y2);

        NSDecimalMultiply(&z, &four, &firstArctangent, NSRoundBankers);
    }
    else
    {
        BOOL shouldSubtract = YES;
        for (NSInteger n = 3; n < 150; n += 2) {
            NSDecimal numerator;
            if (NSDecimalPower(&numerator, &x, n, NSRoundBankers) == NSCalculationUnderflow)
            {
                numerator = DDDecimalZero();
                n = 150;
            }

            NSDecimal denominator = DDDecimalFromInteger(n);

            NSDecimal term;
            if (NSDecimalDivide(&term, &numerator, &denominator, NSRoundBankers) == NSCalculationUnderflow)
            {
                term = DDDecimalZero();
                n = 150;
            }

            if (shouldSubtract) {
                NSDecimalSubtract(&z, &z, &term, NSRoundBankers);
            } else {
                NSDecimalAdd(&z, &z, &term, NSRoundBankers);
            }

            shouldSubtract = !shouldSubtract;
        }
    }

    return z;
}
nsdecimaldddecimalatan(nsdecimalx){
//发件人:http://en.wikipedia.org/wiki/Inverse_trigonometric_functions#Infinite_series
//如果x>1,则正规无穷级数发散
nsdecimalone=DDDecimalOne();
nsdecimalabsx=DDDecimalAbsoluteValue(x);
nsz=x;
if(NSDecimalCompare(&one,&absX)=取消搜索)
{
//y=x/(1+sqrt(1+x^2))
//Atan(x)=2*Atan(y)
//发件人:http://www.mathkb.com/Uwe/Forum.aspx/math/14680/faster-Taylor-s-series-of-Atan-x
根的内部;
NSDecimalMultiply(&interioroftroot,&x,&x,NSRoundBankers);
NSDecimalAdd(根的内部和一根的内部和根的内部,NSRONDBANKERS);
NSDecimal分母=DDDecimalSqrt(根的内部);
NSDecimalAdd(分母和一个分母和一个分母);
NSY;
NSDecimalDivide(&y,&x,&Deminator,NSR);
NSDecimalMultiply(&interioroftroot,&y,&y,NSRoundBankers);
NSDecimalAdd(根的内部和一根的内部和根的内部,NSRONDBANKERS);
分母=DDDecimalSqrt(根的内部);
NSDecimalAdd(分母和一个分母和一个分母);
NSY2;
NSDecimalDivide(&y2,&y,&Deminator,NSR);
//nsdecimaltwo=DDDecimalTwo();
NSDecimal四=DDDECIMAL整数(4);
NSDecimal第一反正切=DDDecimalAtan(y2);
NSDecimalMultiply(&z,&four,&firstArctangent,nsr);
}
其他的
{
BOOL shouldSubtract=是;
对于(n积分n=3;n<150;n+=2){
十进制分子;
if(NSDecimalPower(&分子,&x,n,NSRoundBankers)==NSCalculationUnderflow)
{
分子=DDDecimalZero();
n=150;
}
NSDecimal分母=DDDecimalFromInteger(n);
小数项;
if(NSDecimalDivide(&术语、分子和分母,NSRoundBankers)==NSCalculationUnderflow)
{
术语=小数点零();
n=150;
}
如果(应该减去){
NSDecimalSubtract(&z,&z,&term,nsr);
}否则{
NSDecimalAdd(&z,&z,&term,NSRONDMALADD);
}
shouldSubtract=!shouldSubtract;
}
}
返回z;
}
这使用了泰勒级数近似,带有一些加快收敛速度的快捷方式。我相信在非常接近Pi/4弧度的结果中,精度可能不是完整的34位数字,所以我可能仍然需要修正这个问题


如果您需要极高的精度,这是一个选项,但同样地,您报告的不应该是双值的,所以这里有些奇怪。

经常使用角度?不,你没有。在我见过的10次开发人员使用角度的例子中,有7次他应该使用线性代数来代替,并且避免使用任何三角函数

旋转最好使用矩阵,而不是角度。另见这个问题:


您需要比1E-15弧度更高的精度吗?真的吗?可能是重复的不,不是重复的。这是关于iOS和其他系统的atan2问题的一般性问题。在这里,我不是在要求解决上面列出的问题(因为我的解决方法不同),我只是为了了解。我认为其他开发人员有时也会遇到类似的困难,所以这是一个一般性的问题。如果你有一个重复的例子,
atan2
产生了错误的答案,在任何平台上,向平台供应商提交一个bug。总是。bugreport.apple.com在这种情况下。如果你打算(听起来很荒谬)声称atan2的错误为+/-0.05,你应该给出一些产生如此大错误的输入值的具体示例。在当前的ARM iOS设备上,
long double
只是映射到
double
,所以用这个你不会得到任何精度。你将在iOS模拟器中获得额外的精度,因为它在Mac上运行