我希望编写自己的power函数来处理NSDecimalNumbers和非整数的指数。我首先尝试使用牛顿方法和内置整数幂方法的组合,但由于牛顿方法我得到溢出错误,当我有超过2位小数的指数。所以我想也许漂浮值pow函数可能是我自己的函数的一个很好的模型。所以我想知道是否有人知道我在哪里可以喜欢关于pow功能内部工作的某些文档?
编辑:
@ wombat57,这些链接看起来像我正在寻找的东西,但是我不知道他们会阅读它们。你建议的算法实际上就是我正在使用的算法。由于指数非常大,溢出来自牛顿方法。因为我得到十进制形式的指数,我必须先将其转换为分数。在我的代码中,这是唯一的方法,将十进制乘以十,直到你有一个整数,然后用它作为分子。这样做可以获得具有3个或更多小数的数字的指数为100+。这会导致溢出错误。
答案 0 :(得分: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
我从这个问题得到了链接,其中有一堆相关的讨论
此页面描述了一种算法: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:
Newton的方法涉及将您当前的猜测提升到您试图找到的根的力量。如果功率很大,并且猜测甚至有点太高,则可能导致溢出。这里的一个解决方案是识别这种情况。如果发生溢出,这意味着猜测太高了。您可以通过以下方式解决问题(每当猜测导致溢出时),将当前猜测设置为最后一次未溢出的猜测与当前猜测之间的值(您可能需要多次这样做)。也就是说,每当Newton的方法溢出时,就进行二进制搜索,直到最后一次没有溢出的猜测。这是一些实现所有这些的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
我应该补充说,可能有更好的解决方案。这确实有效,但是
答案 1 :(得分: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()。
答案 2 :(得分: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;
如果有人对我的代码有任何疑问,我很乐意回答。