deXo-fan Ответов: 4

Моя дубинка или дубинка


Привет,

GeoGrebra, C++ и C# все согласны с тем, каким должен быть cosh(16.612). Но когда я использую функцию, которую я написал сам, которая использует алгоритм для вычисления гиперболического Косинуса, я получаю (очень немного) другой результат. Я попытался настроить обработку с плавающей запятой Visual Studio на точную, строгую и быструю, но безрезультатно.

Что же это за проблема?
Это то, о чем все остальные согласны:
cosh(16.612000) = 8193509.0494933873414993

Это мой первый подход:
_cosh(16.612000) = 8193509.0494933854788542

И мой второй:
ecosh(16.612000) = 8193509.0494933798909187

Последний использует cosh(x) = 0.5(e^x + e^(-x))
Вот мой код:

#include <cstdio>
#include <iostream>
#include <string>

long double e = 2.718281828459045090795598298427648842334747314453125L;

long double ecosh(long double x) {
	//return 0.5 * (exp(x) + exp(-x));
	return 0.5L * (pow(e, x) + pow(e, -x));
}


using namespace std;

using decimal = double;

constexpr bool b(int i) { return i > 0; }

static_assert(b(7), "hej");

auto f = [=](double x) {
	return sqrt(x*x*x);
};

long double fac(long double x) {
	if ((int)x > 1)
		return x * fac(x - 1);

	return 1;
}

//25/2 + 25/4 - 25/2/2*1.5 + 25/2/2/2*1.5
decimal _sqrt(decimal x, int iterations = 15) {
	decimal y = x - x / 2 + x / 4;

	for (int i = 1; i < 49; i++) {
		y += x / pow(2, i) * 1.5 * pow(-1, i);
	}
	return y+0.0000000001;
}

long double _cosh2(double x) {
	long double y = 0;
	for (int i = 1; i < 70; i++) {
		y += pow(x, (double)(2.0*i)) / fac(2.0*i);
	}
	return y+1;
}

long double _sinh2(double x) {
	long double y = x;
	for (int i = 1; i < 70; i++) {
		y += pow(x, (double)(2.0*i+1)) / fac(2.0*i+1);
	}
	return y + 0;
}

int main(int argc, char** argv)
{
	double r = 4.0;
	printf("sqrt(%f) = %.25f\n\n", r, _sqrt(r));
	printf("sqrt(%f) = %.25f\n\n", r, sqrt(r));
	double d = 16.612;
	printf("_cosh(%f) = %.16f\n cosh(%f) = %.16f\n", d, _cosh2(d), d, cosh(d));
	printf("ecosh(%f) = %.16lf\n", d, ecosh(d));
	printf("--------------------------------------\n");
	printf("e() = %.75lf\ne   = %.75f\n", exp(1.0L), e);
	printf("                       %.50f\n", _sinh2(6.125));
	printf("                       %.50f\n", sinh(6.125));
	getchar();
}


Что я уже пробовал:

Я попытался перенастроить Visual Studio 2017.

nv3

Сколько цифр точности вы ожидаете при вычислении типа double?

Dave Kreskowiak

Это не имеет никакого отношения к каким-либо настройкам в Visual Studio. Это все зависит от вашего кода.

Нет, я понятия не имею, что это будет.

[no name]

Вопрос в том, почему вы получаете другой результат, чем другие? Помимо чисто тривиальных ошибок, это тонкий вопрос. Написание математической библиотеки-очень сложная задача, и до сих пор вы смотрели на нее очень поверхностно. Вы ничего не знаете о том, как другие методы вычисляют это внутренне или насколько они сами близки к "правильному" результату. В частности, вы сами, кажется, не провели тщательного анализа. Например, что такое pow ()? Я бы добавил, что результат, который вы получите, будет казаться совершенно адекватным для подтверждения работы алгоритма.

YvesDaoust

Зачем волноваться ? Ваша ценность точна до пятнадцати значимых цифр, максимум, который обеспечивает двойная точность.

4 Ответов

Рейтинг:
36

Rick York

Я попробовал вашу формулу на моем маленьком испытательном стенде, который использует VS17, и получил точно такие же результаты, как и вы. Я попробовал различные значения для количества итераций от 32 до 128, и все они дали один и тот же результат. FWIW, выше примерно 127 дает NAN.

Я думаю, что вы сталкиваетесь с числовыми ограничениями 64-битной математики с плавающей точкой. Это усугубляется использованием двух итерационных вычислений для вычисления ответа. В обоих расчетах используется постоянное значение, которое не может быть представлено точно (1 и счетчик циклов). Эта небольшая погрешность в приближении затем увеличивается с каждой итерацией цикла. В конце концов, вы точны до 15 цифр, и это действительно хорошо, так как 64-битные значения с 52-битной мантиссой, как говорят, имеют от 15 до 17 значащих цифр точности в соответствии с Википедией.

Одна вещь, которую следует иметь в виду, - это то, что длинный двойник рассматривается как двойной VS2017.


Рейтинг:
26

Daniel Pfeffer

Написать хорошую математическую библиотеку очень трудно. Библиотека должна не только давать точные результаты, но и сохранять другие атрибуты, такие как монотонность (т. е. если математическая функция монотонна, ваше приближение также должно быть монотонным), нечетное/четное поведение (т. е. f(x) == f(-x) или f(x) == -f(-x) для определенных функций) и т. д.

Например:
1. Беспомощны() функция четная, т. е. дубинка(X) == дубинка(-х). Чтобы гарантировать это, многие математические библиотеки вычислят y = x^2, cosh(x) == F(y).
2. Функция sinh() нечетна, т. е. sinh(x) == -sinh(-x). Чтобы гарантировать это, многие математические библиотеки вычислят y = x^2, sinh(x) = x * G(y).

Другие вопросы, такие как редукция аргументов, вычисление функции в ограниченном диапазоне и восстановление результата, слишком сложны, чтобы вдаваться в них. Хорошая книга на эту тему есть Элементарные функции - алгоритмы и реализация | Жан-Мишель Мюллер | Спрингер[^]. Вы также можете посмотреть исходный код для качественной библиотеки по адресу fdlibm[^].


deXo-fan

Я знаю о четных/нечетных вещах, но я не совсем понял, что вы имели в виду, вычисляя y как x^2 и cosh(x) == F(y).
Ф, вы имеете в виду интегрированную Ф? Стволовая функция?

Daniel Pfeffer

У нас есть четная функция f(x). Мы хотим разработать приближение F(x) к f.

При разработке аппроксимации четной функции иногда можно получить нечетные компоненты с очень малым коэффициентом. Это происходит из-за округления ошибок в расчетах, и они портят равномерность приближения.

Один из способов избежать этого-заменить " x " на "x^2" и вычислить a различный приближение к f, используя новый параметр. Это гарантирует, что приближение также является четным.

Рейтинг:
13

KarstenK

если вам нужна более высокая точность, вы можете использовать некоторую математическую библиотеку, например Увеличить.математика и прочел: статья Википедии В нем также есть несколько интересных ссылок.

Вы также должны прочитать эту статью о четырехкратная точность вычисления, потому что у него есть какой-то пример кода.

Замечание: может быть, лучше написать

decimal y = x - x / 2. + x / 4.;
чтобы избежать проблем с округлением.


Рейтинг:
1

deXo-fan

Большое вам всем спасибо. Должен признаться, что я не собирался писать математическую библиотеку, мне было просто любопытно. Но я все еще заинтригован, и я обязательно посмотрю все библиотеки, которые вы, ребята, предложили, и эту книгу, предложенную Даниэлем Пфеффером.

Я желаю вам всего наилучшего.