我当然知道cmath(math.h)中有一个很好的pow()函数,但不接触pow()的背景什么是最快的方法来计算数字用我自己的双手?
您在问两个不同的问题:
这些有不同的答案。
pow
很快。标准库的实现通常由非常聪明的人编写,由其他聪明的人审查,然后由更聪明的人重构。因此,使用提供的标准库实现几乎总是比尝试自己重新实现标准库更好。
但是,如果您坚持创建自己的
pow
实现,则应首先使用泰勒级数展开来实现 exp
和 log
。然后使用以下属性:
pow(base,power) = exp( power * log(base) )
请注意,如果
base
为负数,则应首先计算 pow(-base,power)
,然后对 base
进行奇偶校验以确定结果的符号。
在我的系统中,Visual Studio 中的标准 pow() 函数的运行速度比我使用 x87 指令编写的简单函数几乎慢 1.4 倍。但我努力工作,使用 AVX 指令编写了几乎同样精确的函数,它的运行速度比标准 pow() 快约 2.5 倍。下面我提供了两个版本的代码(x86架构)。
FPU代码
_declspec(naked) double _fastcall pwr(const double &x, const double &y)
{
_asm
{
fld qword ptr [edx] // Load exponent y
fld qword ptr [ecx] // Load base x
fyl2x // Calculate y*lb(x). This will throw an error if x<0
fxam // Check result type to reveal values +-Inf
fnstsw ax // Move the swr register into ax (there is the check result)
sahf // Set cf if y*lb(x) is equal +-Inf or NaN
fld1 // On stack: y*lb(x); 1
fld st(1) // On stack: y*lb(x); 1; y*lb(x)
frndint // В st(0) - exponent rounded to whole numbers (yi)
fsub st(2),st(0) // в st(2) - fraction of yf in the range from -0.5 to 0.5
fxch st(2) // On stack: yi; 1; yf
fcmovb st(0),st(1) // In case |y*lb(x)|=Inf replace yf (equal NaN) by 1
f2xm1 // Calculate 2^yf-1
faddp st(1),st(0) // Add 1 to result, we get 2^yf
fscale // Take into account the whole part by scaling
fstp st(1) // Remove yi from stack, результат в st(0)
ret // Return
}
}
AVX 代码 (稍后会...)