我正在尝试以前在 this 中描述的快速 Exp(x) 函数回答关于提高 C# 计算速度的 SO 问题:
public static double Exp(double x)
{
var tmp = (long)(1512775 * x + 1072632447);
return BitConverter.Int64BitsToDouble(tmp << 32);
}
该表达式使用了一些 IEEE 浮点“技巧”,主要用于神经集中。该函数比常规 Math.Exp(x)
函数快大约 5 倍。
不幸的是,相对于常规 Math.Exp(x)
函数,数值精度仅为 -4% -- +2%,理想情况下我希望精度至少在 sub-百分比范围。
我绘制了近似和常规 Exp 函数之间的商,从图中可以看出,相对差异似乎以几乎恒定的频率重复。
是否可以利用这种规律性进一步提高“fast exp”函数的准确性,而不会显着降低计算速度,或者提高准确性的计算开销是否会超过原始表达式的计算增益?
(作为旁注,我还尝试了在同一个 SO 问题中提出的 one of the alternative 方法,但这种方法在 C# 中似乎计算效率不高,至少在一般情况下不是这样。)
5 月 14 日更新
应@Adriano 的要求,我现在执行了一个非常简单的基准测试。我已经使用每个替代 exp 函数对 [-100, 100] 范围内的浮点值执行了 1000 万次计算。由于我感兴趣的值范围从 -20 到 0,我还明确列出了 x = -5 处的函数值。以下是结果:
Math.Exp: 62.525 ms, exp(-5) = 0.00673794699908547
Empty function: 13.769 ms
ExpNeural: 14.867 ms, exp(-5) = 0.00675211846828461
ExpSeries8: 15.121 ms, exp(-5) = 0.00641270968867667
ExpSeries16: 32.046 ms, exp(-5) = 0.00673666189488182
exp1: 15.062 ms, exp(-5) = -12.3333325982094
exp2: 15.090 ms, exp(-5) = 13.708332516253
exp3: 16.251 ms, exp(-5) = -12.3333325982094
exp4: 17.924 ms, exp(-5) = 728.368055056781
exp5: 20.972 ms, exp(-5) = -6.13293614238501
exp6: 24.212 ms, exp(-5) = 3.55518353166184
exp7: 29.092 ms, exp(-5) = -1.8271053775984
exp7 +/-: 38.482 ms, exp(-5) = 0.00695945286970704
ExpNeural 等同于本文开头指定的 Exp 函数。 ExpSeries8 是 formulation我最初声称在 .NET 上效率不高;当完全像 Neil 一样实现它时,它实际上非常快。 ExpSeries16 是类似的公式,但有 16 次乘法而不是 8 次。exp1 到 exp7 是与下面 Adriano 的回答不同的函数。 exp7 的最终变体是检查 x 符号的变体;如果为负,则函数返回 1/exp(-x)
。
不幸的是,Adriano 列出的 expN 函数都没有在我考虑的更广泛的负值范围内就足够了。 Neil Coffey的级数扩展方法似乎更适合“我的”值范围,尽管它与较大的负 x 发散太快,尤其是在“仅”使用 8 次乘法时。
最佳答案
泰勒级数近似值(例如 Adriano's answer 中的 expX()
函数)在接近零时最准确,在 -20 甚至 -5 时可能会有很大的误差。如果输入具有已知范围,例如像原始问题一样的 -20 到 0,您可以使用一个小的查找表和一个额外的乘法来大大提高准确性。
诀窍是认识到 exp() 可以分为整数和小数部分。例如:
exp(-2.345) = exp(-2.0) * exp(-0.345)
小数部分始终介于 -1 和 1 之间,因此泰勒级数近似值将非常准确。整数部分只有 exp(-20) 到 exp(0) 的 21 个可能值,因此这些可以存储在一个小的查找表中。
关于c# - 快速 Exp 计算 : possible to improve accuracy without losing too much performance?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10552280/