我一直想知道如何制作一个自己计算功率(例如2 3)的功能.在大多数语言中,这些都包含在标准库中,主要是作为pow(double x, double y),但我怎么能自己编写呢?
我在考虑for loops,但它认为我的大脑进入了一个循环(当我想用非整数指数做一个力量,比如5 4.5或负2-2)并且我疯了;)
那么,我该如何编写一个计算实数幂的函数呢?谢谢
哦,也许重要的是要注意:我不能使用功能(例如exp)的功能,这将使这最终无用.
for*_*ran 48
负功率不是问题,它们只是1/x正功率的反转().
浮点功率稍微复杂一些; 如你所知,分数幂相当于一个根(例如x^(1/2) == sqrt(x)),你也知道用相同的基数乘以幂相当于加上它们的指数.
综上所述,您可以:
例:
2^(-3.5) = (2^3 * 2^(1/2)))^-1 = 1 / (2*2*2 * sqrt(2))
Run Code Online (Sandbox Code Playgroud)
Jer*_*fin 22
A B = Log -1(Log(A)*B)
编辑:是的,这个定义确实提供了一些有用的东西.例如,在x86上,它几乎直接转换为FYL2X(Y*Log 2(X))和F2XM1(2 x -1):
fyl2x
fld st(0)
frndint
fsubr st(1),st
fxch st(1)
fchs
f2xmi
fld1
faddp st(1),st
fscale
fstp st(1)
Run Code Online (Sandbox Code Playgroud)
代码的结束时间比您预期的要长一些,主要是因为F2XM1只能使用-1.0..1.0范围内的数字.这fld st(0)/frndint/fsubr st(1),st件作品减去整数部分,所以我们只留下了分数.我们应用于F2XM1此,重新添加1,然后用于FSCALE处理取幂的整数部分.
Ste*_*non 19
通常pow(double, double),数学库中函数的实现基于以下标识:
pow(x,y) = pow(a, y * log_a(x))
Run Code Online (Sandbox Code Playgroud)
使用此标识,您只需要知道如何将单个数字a提升为任意指数,以及如何采用对数基数a.您已经将复杂的多变量函数有效地转换为单个变量和乘法的两个函数,这很容易实现.最常见的选择值a是e或2- e因为e^x和log_e(1+x)有一些非常漂亮的数学特性,并且2因为它具有用于浮点运算执行一些不错的性能.
做这种方式的缺点是,(如果你想获得完全精确),你需要计算的log_a(x)项(以及它与产品y)比的浮点表示更高的精度x和y.例如,如果x并且y是双精度数,并且您希望获得高精度结果,则需要采用某种方式以更高精度的格式存储中间结果(并进行算术运算).英特尔x87格式是常见的选择,64位整数也是常见的选择(尽管如果你真的想要一个高质量的实现,你需要做一些96位整数计算,这在某些方面有点痛苦语言).如果你实现powf(float,float),处理这个更容易,因为那样你就可以使用了double用于中间计算.如果你想使用这种方法,我建议从那开始.
我概述的算法不是唯一可行的计算方法pow.它仅仅是最适合于提供满足固定的先验精度界限的高速结果.它在其他一些情况下不太适合,并且实际上比其他人提出的重复平方[root] -ing算法更难实现.
如果你想尝试重复的square [root]算法,首先要编写一个仅使用重复平方的无符号整数幂函数.一旦你很好地掌握了减少的算法的算法,你会发现扩展它来处理小数指数是相当简单的.
有两种不同的情况需要处理:整数指数和小数指数.
对于整数指数,您可以通过平方使用取幂.
def pow(base, exponent):
if exponent == 0:
return 1
elif exponent < 0:
return 1 / pow(base, -exponent)
elif exponent % 2 == 0:
half_pow = pow(base, exponent // 2)
return half_pow * half_pow
else:
return base * pow(base, exponent - 1)
Run Code Online (Sandbox Code Playgroud)
第二个"elif"区别于天真的pow功能.它允许函数进行O(log n)递归调用而不是O(n).
对于小数指数,您可以使用标识a ^ b = C ^(b*log_C(a)).取C = 2很方便,所以a ^ b = 2 ^(b*log2(a)).这减少了为2 ^ x和log2(x)编写函数的问题.
采用C = 2很方便的原因是浮点数存储在base-2浮点中.log2(a*2 ^ b)= log2(a)+ b.这样可以更容易地编写log2函数:您不需要对每个正数都准确,只需在区间[1,2]上.类似地,要计算2 ^ x,您可以乘以2 ^(x的整数部分)*2 ^(x的小数部分).第一部分存储在浮点数中是微不足道的,对于第二部分,您只需要在区间[0,1]上使用2 ^ x函数.
困难的部分是找到2 ^ x和log2(x)的良好近似值.一种简单的方法是使用泰勒级数.
根据定义:
a ^ b = exp(b ln(a))
哪里 exp(x) = 1 + x + x^2/2 + x^3/3! + x^4/4! + x^5/5! + ...
哪里n! = 1 * 2 * ... * n.
实际上,您可以存储前10个值的数组1/n!,然后进行近似
exp(x) = 1 + x + x^2/2 + x^3/3! + ... + x^10/10!
因为10!是一个巨大的数字,所以1/10!非常小(2.7557319224⋅10^ -7).