Экспоненциальная аппроксимация не подходит для малого или большого входа

Код экспоненциальной аппроксимации на основе ряда портных https://en.wikipedia.org/wiki/Taylor_series хорошо работает для входов около нуля, но совершенно бесполезен при перемещении дальше в любом направлении. Ниже приведен вывод моего маленького тестового кода, который вычисляет exp для входных данных в диапазоне от -12 до 12 и печатает ошибку по сравнению с результатами std :: exp, а ошибки на границах огромны. Например ошибка для -12 ввода о колоссальных 148255571469%:

in = -12 error = 148255571469.28%
in = -11.00 error = 18328703925.31%
in = -10.00 error = 2037562880.10%
in = -9.00 error = 199120705.27%
in = -8.00 error = 16588916.06%
in = -7.00 error = 01128519.76%
in = -6.00 error = 00058853.00%
in = -5.00 error = 00002133.29%
in = -4.00 error = 00000045.61%
in = -3.00 error = 00000000.42%
in = -2.00 error = 00000000.00%
in = -1.00 error = 00000000.00%
in = 0.00 error = 00000000.00%
in = 1.00 error = 00000000.00%
in = 2.00 error = 00000000.00%
in = 3.00 error = 00000000.00%
in = 4.00 error = 00000000.03%
in = 5.00 error = 00000000.20%
in = 6.00 error = 00000000.88%
in = 7.00 error = 00000002.70%
in = 8.00 error = 00000006.38%
in = 9.00 error = 00000012.42%
in = 10.00 error = 00000020.84%
in = 11.00 error = 00000031.13%
in = 12.00 error = 00000042.40%

Мне нужно приблизить с погрешностью менее 1% ошибки по всему диапазону как можно больше. Есть идеи как этого добиться?

Мой маленький тестовый код ниже:

    #include <cmath>
#include <iostream>
#include <iomanip>

double my_exp(double x) //based on https://en.wikipedia.org/wiki/Exponential_function
{
double res = 1. + x, t = x;
unsigned long factorial = 1;
for (unsigned char i = 2; i <= 12; ++i)
{
t *= x, factorial *= i;
res += t / factorial;
}
return res;
}

int main(int argc, char* argv[])
{
for (double in = -12; in <= 12; in += 1.)
{
auto error = std::abs(my_exp(in) - std::exp(in));
auto percent = error * 100. / std::exp(in);
std::cout << "in = " << in << " error = "<< std::fixed << std::setw( 11 ) << std::setprecision( 2 )
<< std::setfill( '0' ) << percent << "%" << std::endl;
}
return 0;
}

Решения из, казалось бы, похожего вопроса из «Приблизительно е ^ х» Приблизительно е ^ х не решает эту проблему:

  • Решения, основанные на приближении Ремеза и Паде, обеспечивают точность только в ограниченном диапазоне (https://stackoverflow.com/a/6985347/5750612)
  • e ^ x = 2 x / ln (2) сводится к приближению pow, и я не смог найти точного
  • Портной серия не работает для малых и больших входов
  • Решение expf_fast дает более равномерную ошибку во всем диапазоне, но оно все еще слишком велико (~ 20% в конце диапазона)

0

Решение

Один из самых простых способов найти приближение с помощью расширения Тейлора — проверить скорость сходимости, просто добавив новый более высокий член и подтвердив результат.

Вот мой код на C ++ для поддержки вышеуказанных идей.

#include <iostream>
#include <cmath>

double my_exp(const double x, const double x0 = 0, const double ncut = 1e-3)
{
double res = 1.;
double addterm = (x - x0);
size_t norder = 1;

while(true)
{
double res_update = res + addterm;
if(std::abs(res_update - res)/std::abs(res) < ncut){
break;
}
norder += 1;
addterm *= (x - x0)/norder;
res = res_update;
}

return res;
}int main(int argc, char* argv[])
{
const double x = std::atof(argv[1]);
const double approxi = my_exp(x);

const double exactResult = std::exp(x);

std::cout<<"approxi : "<< approxi<<std::endl;
std::cout<<"exact : "<< exactResult<<std::endl;

std::cout<<"err: "<< (1 - std::abs(approxi/exactResult))*100 <<std::endl;

return 0;
}
1

Другие решения

Других решений пока нет …