2012-01-07 88 views
2

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

我知道理论上atan2是精确的,但在我的系统(iOS)中,它对于0.05弧度是不准确的,所以它有很大的不同。这不仅仅是我的问题。我见过类似的意见。

+1

你需要比1E-15弧度更精确?真? – 2012-01-07 12:11:14

+0

可能重复的[CGAffineTranformRotate atan2不准确](http://stackoverflow.com/questions/8766128/cgaffinetranformrotate-atan2-inaccuration) – jrturton 2012-01-07 13:03:47

+0

不,它不重复。这是关于iOS和其他系统的atan2问题的一般问题。在这里,我并没有要求解决上述问题的解决方案(因为我已经以不同的方式解决了问题),我只是为了了解而问。我认为其他开发者有时会遇到类似的困难,所以这是一个普遍的问题。 – wczekalski 2012-01-07 14:55:19

回答

2

如果您的系统中的long doubledouble更精确,您可以使用atan2l

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

在目前的ARM iOS设备上,'long double'只是映射到'double',所以使用它不会获得任何精度。您将在iOS模拟器中获得更高的精度,因为它在Mac上运行。 – 2012-01-09 19:30:56

3

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

编辑。如果您确实需要(x,y)的角度,则可以使用CORDIC的变体来计算所需的精度。

2

在iOS上,我发现标准的三角函数运算符精确到13或14位十进制数,所以听起来很奇怪,你看到0.05弧度量级的错误。如果您可以生成代码和具体的数值来证明这一点,请在行为上提供file a bug report(并在此处发布代码以便我们可以记录它)。这就是说,如果你真的需要三角算子的高精度,我已经修改了Dave DeLong为他的DDMathParser代码创建的一些例程。这些例程使用NSDecimal来执行数学运算,给出高达约34位的小数精度,同时避免标准浮点问题,代表基数为10的小数。您可以从here下载这些修改的例程的代码。的atan()

的NSDecimal版本使用下面的代码来计算:

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; 
} 

这里使用泰勒级数逼近,一些快捷键的速度收敛。我相信精度可能不是完全的34位数,非常接近Pi/4弧度,所以我可能仍然需要解决这个问题。

如果您需要极高的精度,这是一个选项,但您的报告不应该与double的值一起发生,所以这里有些奇怪。

2

经常使用角度?不,你没有。在我看到一个开发人员使用角度的10次中,有7次他应该使用线性代数来避免任何三角函数。

旋转最好用矩阵完成,而不是用角度完成。也看到这个问题:

CGAffineTranformRotate atan2 inaccuration

+0

我希望我可以给这个答案10ooo投票!在我看来,当你使用atan2计算角度(即知道delta_x和delta_y)时,三角函数滥用的比例接近95%... – gboffi 2016-11-30 08:40:37