2014-06-11 118 views
0

我正在尋找寫我自己的權力函數來處理NSDecimalNumbers和非整數的指數。我首先嚐試使用牛頓方法和內置整數冪方法的組合,但由於牛頓方法,當我有超過2位小數的指數時,出現溢出錯誤。所以我想可能float函數pow函數可以作爲我自己函數的一個很好的模型。所以我想知道是否有人知道我在哪裏可以找到關於pow函數內部工作的一些文檔?pow函數是如何工作的?

編輯:

@ wombat57,這些鏈接看起來像他們可能是我所期待的,但是我不知道去閱讀。你所建議的算法實際上就是我正在使用的。由於非常大的指數,溢出來自牛頓法。因爲我以十進制形式獲得指數,所以我必須先將它轉換爲小數。就我所知,用代碼來表示這種情況的唯一方法是將十進制數乘以十,直到得到整數,然後用它作爲分子。這樣做,你可以得到100+的指數,並且有3位或更多的小數。這會導致溢出錯誤。

+0

剛剛添加的代碼應該適合你 – wombat57

回答

1

編輯1:這裏是鏈接到實際源

http://opensource.apple.com/source/Libm/Libm-2026/Source/Intel/expf_logf_powf.c http://opensource.apple.com/source/Libm/Libm-315/Source/ARM/powf.c

我得到了這個問題,它有一堆相關的討論

self made pow() c++

這的鏈接頁面描述了一個算法:http://mathforum.org/library/drmath/view/55896.html。 x ^(1/n)= x的第n個根,並且x^mn =(x^m)^ n。因此,x ^(m/n)=(x的第n個根)^ m。任意根可以用牛頓法來計算。整數冪可以通過平方冪來計算。對於非理性指數,您可以使用越來越準確的有理數近似值,直到獲得所需的有效數字數。

編輯2:

牛頓的方法是提高當前的猜測到你要找到根的力量。如果這個能力很大,而且猜測甚至有點太高,這可能會導致溢出。這裏的一個解決方案是識別這種情況。如果溢出發生,這意味着猜測值太高。您可以通過(每當猜測結果溢出時)將當前猜測值設置爲上一次未溢出猜測值與當前猜測值之間的值(可能需要多次執行此操作)來解決問題。也就是說,每當牛頓的方法溢出時,都要執行二進制搜索,直到最後一次沒有溢出的猜測。下面是一些Python實現這一切:

def nroot(n, b, sig_figs = 10): 
    g1 = 1.0 
    g2 = 1.0 
    while True: 
     done = False 
     while not done: 
      try: 
       g3 = g2 - ((g2**b) - n)/(b * (g2**(b-1))) 
       done = True 
      except OverflowError: 
       g2 = (g1 + g2)/2.0 

     if abs(g2 - g3) < 1.0/(10**sig_figs): 
      return g3 
     g1 = g2 
     g2 = g3 

def npowbysqr(n, p): 
    if p == 0: 
     return 1.0 
    if p % 2 == 0: 
     v = npowbysqr(n, p/2) 
     return v*v 
    else: 
     return n*npowbysqr(n, p-1) 

def npow(n, p): 
    return npowbysqr(nroot(n, 1000000), int(p*1000000)) 

print npow(5, 4.3467) 
print 5**4.3467 

我要補充一點,有可能是更好的解決方案。這似乎工作,但

+0

問題是指數有3個或更多的小數將指數轉換爲小數形式你得到的分子和分母的值超過128這將導致溢出,除非基數小於1那麼當你的第一次猜測導致這種情況時會發生什麼? – JDOdle

+0

第一個猜測是1 – wombat57

+0

好吧,所以可以說真正的價值是9,可以說你的猜測是7左右,即使這個小數字也會產生溢出異常。你會被猜出來。無論如何,我想出了一個可行的解決方案,看看我的帖子。 – JDOdle

0

我碰巧需要這樣的事情前一陣子。謝天謝地,Dave DeLong在他的DDMathParser中一直在修補這個問題,所以我建立了它。他從this commit的代碼中抽出他的實現,但是我接受並修改了它。這是我的版本他NSDecimal冪函數:

extern NSDecimal DDDecimalPower(NSDecimal d, NSDecimal power) { 
    NSDecimal r = DDDecimalOne(); 
    NSDecimal zero = DDDecimalZero(); 
    NSComparisonResult compareToZero = NSDecimalCompare(&zero, &power); 
    if (compareToZero == NSOrderedSame) { 
     return r; 
    } 
    if (DDDecimalIsInteger(power)) 
    { 
     if (compareToZero == NSOrderedAscending) 
     { 
      // we can only use the NSDecimal function for positive integers 
      NSUInteger p = DDUIntegerFromDecimal(power); 
      NSDecimalPower(&r, &d, p, NSRoundBankers); 
     } 
     else 
     { 
      // For negative integers, we can take the inverse of the positive root 
      NSUInteger p = DDUIntegerFromDecimal(power); 
      p = -p; 
      NSDecimalPower(&r, &d, p, NSRoundBankers); 
      r = DDDecimalInverse(r); 
     } 
    } else { 
     // Check whether this is the inverse of an integer   
     NSDecimal inversePower = DDDecimalInverse(power); 
     NSDecimalRound(&inversePower, &inversePower, 34, NSRoundBankers); // Round to 34 digits to deal with cases like 1/3 

     if (DDDecimalIsInteger(inversePower)) 
     { 
      r = DDDecimalNthRoot(d, inversePower); 
     } 
     else 
     { 
      double base = DDDoubleFromDecimal(d); 
      double p = DDDoubleFromDecimal(power); 
      double result = pow(base, p); 
      r = DDDecimalFromDouble(result); 
     }   
    } 
    return r; 
} 

將嘗試識別常見的情況和使用這些更精確的計算。儘管如此,它在pow()方法上不適合這些情況。

我使用的NSDecimal函數的其餘部分可以找到herehere

+0

我實際上是在尋找一種功能,它不會回落到pow以前的狀態。不過謝謝你! – JDOdle

0

我已經想出了一個適合我需求的功能,並希望能滿足許多​​其他需求。以下方法已完全註釋並適用於具有實際價值的任何電源功能。此方法也只使用NSDecimalNumbers,這意味着由於浮動四捨五入錯誤,您不會失去任何精度。這個方法有兩個參數,一個用於基礎,一個用於權力,兩個都是NSDecimalNumbers。所以這裏是:

//these are constants that will be used 
NSDecimalNumber *ten = [NSDecimalNumber decimalNumberWithString:@"10"]; 
NSDecimalNumber *one = NSDecimalNumber.one; 

//these will together hold the power in fractional form 
NSDecimalNumber *numerator = power, *denominator = one; 

//this will hold the final answer and all previous guesses the first guess is set to be the base 
NSDecimalNumber *powAns = base; 

//this will hold the change in your guess, also serves as an idea of how large the error is 
NSDecimalNumber *error = one; 

//part1 holds f(x) and part2 holds f'(x) 
NSDecimalNumber *part1, *part2; 

//if the base is < 0 and the power is not whole, answer is not real 
if ([base doubleValue] < 0 && [[power stringValue] rangeOfString:@"."].location != NSNotFound) 
    return NSDecimalNumber.notANumber; 

//converts power to a fractional value 
while ([[numerator stringValue] rangeOfString:@"."].location != NSNotFound) { 

    numerator = [numerator decimalNumberByMultiplyingBy:ten]; 
    denominator = [denominator decimalNumberByMultiplyingBy:ten]; 
} 

//conditions here are the precision you wish to get 
while ([error compare:[NSDecimalNumber decimalNumberWithString:@"1e-20"]] == NSOrderedDescending || 
     [error compare:[NSDecimalNumber decimalNumberWithString:@"-1e-20"]] == NSOrderedAscending) { 

    //if this catches an overflow error it is set to be a very large number otherwise the value cannot be a number, however no other error should be returned. 
    @try { 
     part1 = [powAns decimalNumberByRaisingToPower:[denominator intValue]]; 
    } 
    @catch (NSException *exception) { 

     if ([exception.name isEqual: NSDecimalNumberOverflowException]) 
      part1 = [NSDecimalNumber decimalNumberWithString:@"10e127"]; 

     else 
      return NSDecimalNumber.notANumber; 
    } 

    part1 = [part1 decimalNumberBySubtracting:base]; 

    //if this catches an overflow error it is set to be a very large number otherwise the value cannot be a number, however no other error should be returned. 
    @try { 
     part2 = [powAns decimalNumberByRaisingToPower:[denominator intValue]-1]; 
     part2 = [part2 decimalNumberByMultiplyingBy:denominator]; 
    } 
    @catch (NSException *exception) { 

     if ([exception.name isEqual: NSDecimalNumberOverflowException]) 
      part2 = [NSDecimalNumber decimalNumberWithString:@"10e127"]; 

     else 
      return NSDecimalNumber.notANumber; 
    } 

    //error is the change in the estimated value or y - f(x)/f'(x) 
    error = [part1 decimalNumberByDividingBy:part2]; 
    powAns = [powAns decimalNumberBySubtracting: error]; 
} 

//if the numerator value is negative it must be made positive and the answer is then inverted 
if ([numerator intValue] < 0) { 
    powAns = [powAns decimalNumberByRaisingToPower:abs([numerator intValue])]; 
    powAns = [one decimalNumberByDividingBy:powAns]; 
} 
else 
    powAns = [powAns decimalNumberByRaisingToPower:[numerator intValue]]; 

return powAns; 

如果任何人有任何關於我的代碼的問題,我很樂意回答他們。

+1

當給定高精度輸入時,這將永遠循環,例如10^2.1111111111。它是這樣做的,因爲當牛頓方法的兩部分都溢出並被限制在最大值時,該系列不會收斂 – wombat57