pow函数是如何工作的?
我正在寻找写我自己的权力函数来处理NSDecimalNumbers和非整数的指数。 我首先尝试使用牛顿方法和内置整数幂方法的组合,但是由于牛顿方法,当我有2位以上小数的指数时,我得到了溢出错误。 所以我想也许浮动值pow函数可以作为我自己的函数的一个很好的模型。 所以我想知道是否有人知道我在哪里可以喜欢pow函数的内部工作的一些文档?
编辑:
@ wombat57,这些链接看起来像他们可能是我正在寻找,但我不知道读它们。 你build议的algorithm实际上是我正在使用的。 由于非常大的指数,溢出来自牛顿法。 因为我用十进制forms得到指数,所以我必须先把它转换成小数。 用代码来表示这个唯一的方法,就我所知,把小数乘以十,直到得到一个整数,然后用它作为分子。 这样做,您可以获得100+的小数点后3位或更多小数的指数。 这会导致溢出错误。
编辑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
我从这个问题中得到了一些相关的讨论
自制pow()c ++
这个页面描述了一个algorithm: 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:
牛顿的方法涉及提高你目前的猜测到你试图find的根的力量。 如果这个能力很大,而且猜测有点太高,这可能会导致溢出。 这里的一个解决scheme是识别这种情况。 如果发生溢出,这意味着猜测太高。 您可以通过(每当猜测结果溢出)解决问题,将当前猜测设置为上一次未溢出的猜测与当前猜测之间的值(您可能必须多次执行此操作)。 也就是说,每当牛顿的方法溢出时,就向上一个没有溢出的猜测进行二分search。 下面是一些实现所有这些的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
我应该补充说,可能有更好的解决scheme。 这似乎工作,但是
我刚才碰巧需要这样的东西。 谢天谢地,Dave DeLong在他的DDMathParser中一直在修补这个问题,所以我就这样做了。 他在这个提交中从他的代码中抽出了他的实现,但是我接受并修改了它。 这是他的NSDecimalfunction函数的版本:
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函数可以在这里和这里find。
我已经想出了一个适合我需求的function,并希望能够满足许多其他需求。 下面的方法是完全注释和适用于任何具有实际价值的权力函数。 此方法也只使用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;
如果有人对我的代码有任何疑问,我很乐意回答。