我一直在学习更快的取幂算法(k-ary,滑动门等),并且想知道在CPU /编程语言中使用哪些算法? (我对这是否发生在CPU或编译器中都很模糊)
只是为了踢,这是最快的?
关于广度的编辑:它有意广泛,因为我知道有很多不同的技术可以做到这一点。已检查的答案符合我的要求。
答案 0 :(得分:3)
我假设您对实现可以在HLL的标准数学库中找到的取幂函数感兴趣,特别是C / C ++。其中包括函数exp()
,exp2()
,exp10()
和pow()
,以及单精度对应expf()
,exp2f()
,{{ 1}}和exp10f()
。
您提到的取幂方法(例如k-ary,滑动窗口)通常用于加密算法,例如RSA,它是基于取幂的。它们通常不用于通过powf()
或math.h
提供的取幂函数。标准数学函数(如cmath
)的实现细节有所不同,但常见的方案遵循三个步骤:
辅助步骤通常是处理特殊情况。这些可能涉及特殊的数学情况,如exp()
,或特殊的浮点操作数,如NaN(非数字)。
以下log(0.0)
的C99代码以示例性方式显示了具体示例的那些步骤。首先将expf(float)
参数拆分为a
= e r * 2 i ,其中exp(a)
是整数且{{ 1}}在[log(sqrt(0.5),log(sqrt(2.0)]中,是主要的近似区间。在第二步中,我们现在用多项式逼近e r 。这样的近似可以根据各种设计标准设计,例如最小化绝对误差或相对误差。多项式可以用各种方式进行评估,包括Horner方案和Estrin方案。
下面的代码采用一种非常常见的方法,采用最小极大近似,最小化整个近似间隔的最大误差。用于计算这种近似的标准算法是Remez算法。评估是通过霍纳的计划;使用i
增强了评估的数字准确性。
这个标准的数学函数实现了所谓的融合乘法加法或FMA。这会在添加期间使用完整产品r
计算fmaf()
,并在结尾处应用单个舍入。在大多数现代硬件上,例如GPU,IBM Power CPU,最近的x86处理器(例如Haswell),最近的ARM处理器(作为可选扩展),这直接映射到硬件指令。在缺少这种指令的平台上,a*b+c
将映射到相当慢的仿真代码,在这种情况下,如果我们对性能感兴趣,我们就不会想要使用它。
最后的计算是乘以2 i ,其中C和C ++提供函数a*b
。在“工业强度”库代码中,通常使用机器特定的习惯用法,其利用了fmaf()
的IEEE-754二进制算法。最后,代码清理溢出和下溢的情况。
x86处理器内的x87 FPU有一条指令ldexp()
,可以在[-1,1]上计算2 x -1。这可用于计算float
和F2XM1
的第二步。在第三步中有一条指令exp()
用于乘以2 i 。实现exp2()
本身的常用方法是使用有理或多项式近似的微码。请注意,x87 FPU主要用于传统支持。在现代x86平台上,库通常使用基于SSE的纯软件实现和类似于下面所示的算法。有些将小表与多项式近似结合起来。
FSCALE
可以在概念上实现为F2XM1
,但是当pow(x,y)
接近统一且exp(y*log(x))
大小时,这会严重损失准确性,以及对C / C ++标准中规定的众多特殊情况的错误处理。解决准确性问题的一种方法是以某种形式的扩展精度计算x
和产品y
。细节将填写一个完整,冗长的单独答案,我没有代码方便地演示它。在各种C / C ++数学库中,log(x)
和y*log(x))
由单独的代码路径计算,该路径应用“square-and-multiply”方法,逐位扫描整数的二进制表示指数。
pow(double,int)