战俘功能如何运作?

时间:2014-06-11 23:28:05

标签: ios objective-c c

我希望编写自己的power函数来处理NSDecimalNumbers和非整数的指数。我首先尝试使用牛顿方法和内置整数幂方法的组合,但由于牛顿方法我得到溢出错误,当我有超过2位小数的指数。所以我想也许漂浮值pow函数可能是我自己的函数的一个很好的模型。所以我想知道是否有人知道我在哪里可以喜欢关于pow功能内部工作的某些文档?

编辑:

@ wombat57,这些链接看起来像我正在寻找的东西,但是我不知道他们会阅读它们。你建议的算法实际上就是我正在使用的算法。由于指数非常大,溢出来自牛顿方法。因为我得到十进制形式的指数,我必须先将其转换为分数。在我的代码中,这是唯一的方法,将十进制乘以十,直到你有一个整数,然后用它作为分子。这样做可以获得具有3个或更多小数的数字的指数为100+。这会导致溢出错误。

3 个答案:

答案 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

我从这个问题得到了链接,其中有一堆相关的讨论

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:

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()。

我使用的其余NSDecimal函数可以找到herehere

答案 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;

如果有人对我的代码有任何疑问,我很乐意回答。