近似e ^ x

时间:2011-08-08 15:22:21

标签: math optimization fpga

我想近似 e x 功能。

是否可以使用基于样条类型的多种方法来实现?即 x 1 x 2 之间,

  

y 1 = a 1 x + b 1 ,x 2 和x 3

然后

  

y 2 = a 2 x + b 2

这适用于专用的fpga硬件,而不是通用CPU。因此我需要自己创建这个功能。准确性不是一个问题。此外,我实际上不能提供多个乘法电路和/或多个移位/加法器。另外,我想要比CORDIC功能小得多的东西,实际上尺寸很重要。

10 个答案:

答案 0 :(得分:23)

使用公式

这样的策略怎么样?
  

e x = 2 x / ln(2)

  1. 预先计算1/ln(2)
  2. 将此常量乘以您的参数(1乘法)
  3. 使用二进制移位将2提升到幂的整数部分(假设exp +尾数格式)
  4. 根据2的剩余幂次数(可能是第二次乘法)进行调整
  5. 我意识到这不是一个完整的解决方案,但它只需要一次乘法,并将剩余的问题减少到接近2的分数幂,这应该更容易在硬件中实现。

    此外,如果您的应用程序足够专业,您可以尝试将在硬件上运行的所有数字代码重新导出到base- e 编号系统中并实现浮动将硬件指向基本 e 。然后根本不需要转换。

答案 1 :(得分:13)

如果x是一个整数,您可以一遍又一遍地乘以e

如果x不是整数,您可以使用上述方法计算 e floor(x) ,然后乘以一个小的修正项。可以使用多种近似方法容易地计算该校正项。一个这样的方式是这样的:

  

e f 1 + f(1 + f/2(1 + f/3(1 + f/4))),其中 f 是x的小数部分

这来自 e x 的(优化的)幂级数展开,对于x的小值非常准确。如果您需要更高的准确性,只需在该系列中添加更多术语。

math.stackexchange问题包含一些其他明智的答案。

编辑:请注意,有一种更快的方法可以计算名为exponentiation by squaring e n

答案 2 :(得分:3)

首先,是什么激发了这种近似?换句话说,直截了当的exp(x)究竟出了什么问题?

尽管如此,exp(x)的典型实现是

  • 查找整数k和浮点数r,使x=k*log(2) + rr介于-0.5 * log(2)和0.5 * log(2)之间。
  • 通过此缩减,exp(x)为2 k * exp(r)
  • 计算2 k 非常容易。
  • exp(x)的标准实现使用Remes类型算法来提出近似exp(r)的minimax多项式。
  • 您可以这样做,但使用降阶多项式。

以下是踢球者:无论你做什么,你的功能都会比调用exp()慢很多,但是几率非常高。 exp()的大部分功能都在您计算机的数学协处理器中实现。即使精度降低,在软件中重新实现该功能也会比使用exp()慢一个数量级。

答案 3 :(得分:2)

或者您可以在C中执行pow(M_E, x)。(某些平台没有定义M_E;在这些平台上,您可能必须手动指定 e 的值,大约是2.71828182845904523536028747135266249775724709369995。)

(正如大卫在评论中指出的那样,exp(x)会比pow(M_E, x)更有效率。再次,大脑尚未开启。)

您是否有一个用例,其中 e x 的计算是一个经过验证的瓶颈?如果没有,你应该首先编写可读性;如果明显的方法太慢,只能尝试这些优化。

答案 4 :(得分:2)

http://martin.ankerl.com/2007/02/11/optimized-exponential-functions-for-java/ 使用Schraudolph的方法(http://nic.schraudolph.org/pubs/Schraudolph99.pdf) 在Java中:

public static double exp(double val) {
    final long tmp = (long) (1512775 * val) + (1072693248 - 60801);
    return Double.longBitsToDouble(tmp << 32);
}

https://math.stackexchange.com/a/56064(寻找Pade近似值)。

答案 5 :(得分:2)

对于硬件,我有一个很棒的解决方案,如果你需要它是比特级准确的。 (另外就像上面那样做一个近似值)。同一性是exp(x)= cosh(x)+ sinh(x),双曲正弦和余弦。可以使用CORIC技术计算双曲线正弦和余弦,最重要的是,它们是FAST CORDIC函数之一,这意味着它们看起来几乎像乘法而不是几乎像鸿沟!

对于数组乘法器的区域,这意味着只需2个周期即可将指数计算为任意精度!

查找CORDIC方法 - 它的硬件实现令人惊叹。

另一种硬件方法是将小表与其他人提到的公式结合使用:exp(x + y)= exp(x)* exp(y)。您可以将数字分成小位字段 - 一次说4位或8位 - 然后只查找该位域的指数。可能只对狭义计算有效,但它是另一种方法。

答案 6 :(得分:1)

Wolfram介绍了一些用系列等方法来近似它的好方法:

Taylor Series上的维基百科页面也显示了e x 在0附近扩展的示例:

答案 7 :(得分:1)

当然这是“可能的”。有几个问题。

  1. 您对准确性的要求是什么?

  2. 您是否愿意使用更高阶的样条线?

  3. 你愿意花多少钱在这上面?在足够小的间隔内的线性函数将近似指数函数达到所需的任何精确度,但它可能需要非常小的间隔。

  4. 编辑:

    鉴于提供的其他信息,我进行了快速测试。范围减少始终可用于指数函数。因此,如果我想为任何x计算exp(x),那么我可以在表单中重写问题......

    y = exp(xi + xf) = exp(xi)*exp(xf)
    

    其中xi是x的整数部分,xf是小数部分。整数部分很简单。以二进制形式计算xi,然后重复的方形和乘法允许您在相对较少的操作中计算exp(xi)。 (其他技巧,使用2和其他间隔的力量可以为你提供更快的速度。)

    现在剩下的就是计算exp(xf)。我们可以使用带有线性段的样条来计算exp(xf),在区间[0,1]上只有4个线性段,精度为0.005吗?

    这个最后一个问题是由我几年前写的一个函数解决的,该函数将使用给定顺序的样条函数近似函数,在最大误差的固定容差范围内。该代码在区间[0,1]上需要8个段,以通过分段线性样条函数实现所需的公差。如果我选择将间隔进一步缩小到[0,0.5],我现在可以达到规定的公差。

    所以答案很简单。如果您愿意进行范围缩减以将x减小到区间[0.0.5],那么进行适当的计算,然后是,您可以使用4个线段中的线性样条线来达到所要求的精度。

    最后,使用硬编码指数函数总是会更好。上面提到的所有操作肯定比编译器提供的速度慢,IF exp(x)可用。

答案 8 :(得分:1)

这不适合自定义FPGA,但值得一提。

http://www.machinedlearnings.com/2011/06/fast-approximate-logarithm-exponential.html

源代码:

https://code.google.com/archive/p/fastapprox/downloads

&#34;更快&#34;实现只涉及3个步骤(乘法,添加,将float转换为int)和最终转换为float。根据我的经验,它准确率为2%,如果您不关心实际值但是在对数似然最大化迭代中使用该值,这可能就足够了。

答案 9 :(得分:1)

这不是您请求的平滑样条插值,但计算效率很高:

float expf_fast(float x) {
   union { float f; int i; } y;
   y.i = (int)(x * 0xB5645F + 0x3F7893F5);
   return (y.f);
}

绘制输出 image