Я пытаюсь выполнить быструю функцию Exp (x), которая ранее была описана в this, отвечая на вопрос SO о повышении скорости вычислений в С#:
public static double Exp(double x)
{
var tmp = (long)(1512775 * x + 1072632447);
return BitConverter.Int64BitsToDouble(tmp << 32);
}
Выражение использует некоторые "трюки" с плавающей запятой IEEE и в основном предназначено для использования в нейронных наборах. Функция примерно в 5 раз быстрее, чем обычная функция Math.Exp(x)
.
К сожалению, числовая точность составляет только -4% - + 2% относительно регулярной функции Math.Exp(x)
, в идеале я хотел бы иметь точность, по крайней мере, в пределах субпроцентного диапазона.
Я построил соотношение между приближенной и регулярной функциями Exp, и, как видно на графике, относительная разность, по-видимому, повторяется с практически постоянной частотой.
Возможно ли использовать эту закономерность для повышения точности функции "fast exp" далее без существенного уменьшения скорости вычисления, или вычислительные накладные расходы по повышению точности перевешивают вычислительное усиление исходного выражения?
(В качестве побочного примечания я также попробовал один из альтернативных вариантов, предложенный в том же вопросе SO, но этот подход, по-видимому, не является эффективным с точки зрения вычислений в С#, по крайней мере, не для общего случая.)
ОБНОВЛЕНИЕ МАЙ 14
По просьбе @Adriano, я сейчас выполнил очень простой тест. Я выполнил 10 миллионов вычислений, используя каждую из альтернативных функций exp для значений с плавающей запятой в диапазоне [-100, 100]. Поскольку диапазон значений меня интересует от -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 - это формулировка , которую я изначально утверждал, не очень эффективен для .NET; при реализации его точно так же, как Нейл, это было очень быстро. ExpSeries16 - это аналогичная формула, но с 16 умножениями вместо 8. exp1 через exp7 - различные функции из ответа Адриано ниже. Окончательный вариант exp7 - это вариант, где проверяется знак x; если отрицательно, функция возвращает 1/exp(-x)
.
К сожалению, ни одна из функций expN, перечисленных Adriano, не достаточна в более широком диапазоне отрицательных значений, который я рассматриваю. Подход к расширению серии Neil Coffey кажется более подходящим в "моем" диапазоне значений, хотя он слишком быстро расходится с большим отрицательным x, особенно при использовании "только", 8 умножений.