2012-01-07 122 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