在浮点中实现pow()函数的最有效方法
我正在尝试实现我自己的pow()和sqrt()函数版本,因为我的自定义库没有pow()/ sqrt()浮点支持。
有人可以帮忙吗?
是的,Sun可以(我现在认为是Oracle):
fdlibm ,“可自由分发的数学库”,具有sqrt和pow ,以及许多其他数学函数。
然而,它们是相当高科技的实现,当然,没有什么是这样的“最有效”的实现。 你是在使用源代码来完成它,还是你真的没有那么多寻找pow
和sqrt
,但实际上是在寻找浮点算法编程的教育?
当然 – 如果你有指数和自然的日志function,这很容易。
由于y = x^n
,您可以采用双方的自然对数:
ln(y) = n*ln(x)
然后采取双方的指数为您提供您想要的:
y = exp(n*ln(x))
如果你想要更好的东西,我所知道的最好的地方是Abramowitz和Stegun 。
请注意,如果您的指令集具有平方根或功率的指令,那么使用它会更好。 例如,x87浮点指令有一条指令fsqrt
,而SSE2的加法包括另一条指令sqrtsd
,它可能比用C编写的大多数解决方案快得多。实际上,至少gcc在编译时使用了两条指令。放在x86机器上。
然而,对于权力来说,事情变得有点模糊。 x87浮点指令集中有一条指令,可用于计算n * log2(n),即fyl2x
。 另一条指令fldl2e
将log2(e)存储在浮点堆栈中。 你可能想看看这些。
您可能还想了解各个C库如何执行此操作。 例如, fsqrt
只使用fsqrt
:
sqrt: fldl 4(%esp) fsqrt ret
glibc
将Sun的实现用于硬件平方根指令不可用的机器(在sysdeps/ieee754/flt-32/e-sqrtf.c
),并在x86指令集上使用fsqrt
(尽管可以指示gcc改为使用sqrtsd
指令。)
使用迭代牛顿方法正确实现平方根。
double ipow(int base, int exp) { bool flag=0; if(exp<0) {flag=1;exp*=-1;} int result = 1; while (exp) { if (exp & 1) result *= base; exp >>= 1; base *= base; } if(flag==0) return result; else return (1.0/result); } //most suitable way to implement power function for integer to power integer
为了计算C中浮点的平方根,如果你定位x86,我建议使用fsqrt
。 您可以使用此类ASM指令:
asm("fsqrt" : "+t"(myfloat));
对于GCC或
asm { fstp myfloat fsqrt fldp myfloat
}
或类似Visual Studio的东西。
为了实现pow,使用像upitasoft.com/link/powLUT.h那样的大开关语句应该这样做。 它可能会导致一些缓存问题,但是如果你保持它不应该是一个问题,只需限制范围(注意,你仍然可以优化我提供的代码)。
如果你想支持浮点功率,那就更难了……你可以尝试使用自然对数和指数函数,例如:
float result = exp(number * log(power));
但通常它很慢和/或不精确。
希望我帮忙。
我可以想到做一个pow()的最快方法是沿着这些方向(注意,这很复杂):
//raise x^y double pow(double x, int y) { int power; map powers; for (power = 1; power < y; power *= 2, x *= x) powers.insert(power, x); while (power > y) { //figure out how to get there map::iterator p = powers.lower_bound(power - y); //p is an iterator that points to the biggest power we have that doesn't go over power - y power -= p->first; x /= p->second; } return x; }
我不知道如何实现十进制功率。 我最好的猜测是使用对数。
编辑:我正在尝试对数解决方案(基于y),而不是您提出的线性解决方案。 让我解决这个问题并编辑它,因为我知道它有效。
编辑2:呵呵,我的坏。 功率* = 2而不是功率++