Pull to refresh

Comments 21

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

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

А вы разве не сделали то же самое?

Почему вы решили не использовать какую-нибудь существующую библиотеку (например, [1])?

Вы будете удивлены но в библиотеке MathNet.Numerics на которую вы ссылаетесь Acot(x) реализован без учета свойства симметрии, т.е. там графическое представление в диапозоне (-pi/2, pi/2) а не (0, pi), я не поленился скачать проверить. Об этом я писал оба варианта корректны, но второй предпочтительнее так как нет разногласия при значении x = 0, я возвращаю pi/2 и в этом нет противоречия, библиотека на которую вы ссылаетесь также возвращает pi/2 а в этом уже есть противоеречие так как при их графике корректно и значение -pi/2. В многих других проектах либо тестов нет, либо нет документации, либо задача решена также в лоб, мне нужна была легкая необременненная лицензиями и протестированная библиотека чтобы включить ее в свой другой проект для расчета математических формул. К сожалению, это встерчается повсеместно, даже самые известные библиотеки могут предоставлять некорректную реализацию. Это момент спорный, кто-то скажет что так корректно, ученый математик скажет что нет, поэтому это продолжает жить, я хотел поднять эту тему и поделиться идеями, чтобы тот кто ищет решение имел возможность задуматься. На самом деле тригонометричексие функции очень важны, и это необходимо обсуждать.

Вы серьёзно? То, что каждый, кто помнит школьную программу, пишет, не задумываясь (по месту или для ясности функциями), вы представляете как значимую доработку стандартной библиотеки?
Не говоря о том, что в компьютерной графике принято использовать не арктангенс с арккотангенсом, а atan2 – функцию двух аргументов, которая по двум компонентам вектора выдаёт угол. Кстати, для неё есть команда FPU, так что это максимально быстрое решение.

Да серьезно, на самом деле очень много людей кто даже если помнит тригонометрию ищет решение этих функций, и часто предлагаются решения в лоб без учета аспектов о которых я писал, про acot, значения близкие к 0 и т.п. Я хотел обратить внимание именно на это, а библиотека вторична, при этом постарался все равно ее сделать с тестами, документацией и удобной для использования, у меня не было задачи переписать то что есть в .NET

Лучше бы обратили внимание, что в стандартной либе есть atan2, и использование acot в большинстве случаев будет ошибкой.
Ну а для тех, у кого много математики – есть либы, включающие в себя не только дополнительные тригонометрические функции, но и более необходимые (и не вычисляющиеся так просто) функции Бесселя и т.п.

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

Реальность: return 1 / Math.Tanh(x);

Посмотрите, пожалуйста, ответ на комментарий выше.

Вообще не факт, что именно такой вариант является универсально правильным, поскольку у него есть разрыв в нуле, что например при численном дифференцировании может преподнести сюрприз. Если уж на то пошло, то можно было добавить ещё один bool параметр, чтобы это пользователь решал, какой вариант ему больше подходит.

Здесь есть проблема в коде. В .NET double.Epsilon — это не то же самое, что DBL_EPSILON из стандартной библиотеки C. Давняя проблема, которая тянется с момента появления .NET.

В C/C++ под эпсилоном понимают максимальную возможную точность. Формальное определение: это такое минимальное число, при котором выполняется неравенство DBL_EPSILON + 1.0 != 1.0.
Используется для сравнения чисел друг с другом, при этом числа не должны быть равны нулю, иначе смысл сравнения теряется.

Если хотите сравнивать с нулём, нужно использовать значение DBL_MIN — это минимальное представимое нормализованное число. Отсекает все ненормализованные числа, приравнивая их к нулю.

В .NET значение double.Epslion — совершенно бесполезное в принципе представимое минимальное число. Оно даже ненормализованное, то есть там значимых бит меньше, чем 52.

Его нельзя использовать ни для чего, в том числе для определения слишком маленьких чисел, потому что чисел меньше не бывает. Оно на 300 порядков меньше, чем DBL_EPSILON. В C/C++ ему соответствует константа DBL_TRUE_MIN.

Самый простой способ поправить код: использовать double.MinValue вместо dboule.Epslion там, где сравниваете с 0, и использовать double.BitIncrement вместо DBL_EPSILON. Это аналог, который появился, если не ошибаюсь, в 6-м .NET Core. Значения констант, чтобы не искать, можно посмотреть в MSDN.

Да вы правы, заменил Math.Abs(x) < double.Epsilon там где идет сравнение с 0 просто на сравнение с 0.0, спасибо, буду знать.
Оставил только Math.Abs(x + 1.0) < double.Epsilon но тут как раз уже тот случай сравнения двух чисел о котором вы писали.

Посчитайте в вашей библиотеке выражение \cos \left(2 \cos ^{-1}(2)\right). Правильный ответ должен быть 7, но скорее всего, вы его не получите. Не все пробелы заполнены стало быть.

Вернет double.NaN
Потому что у Arc cosine входной диапазон [-1, 1]
а у cosine выходной диапазон значений от [-1, 1]
Объясните, пожалуйста, почему должно быть 7

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

Добавил поддержку комплексных чисел.

private static bool IsInfinity(Complex value) { return (value.Imaginary == 0.0) && double.IsInfinity(value.Real); }

Вот тут не понял, это что за логика? Бесконечность с ненулевой мнимой составляющей уже не бесконечность?

Math.Pow(x, 2.0)

Это плохо - намного быстрей получится при использовании просто умножения: x * x.

Я не математик и у меня 3 по алгебре и 4 по геометрии с колледжа, но хочу сказать по поводу оптимизации. В .NET Math и MathF класс внутри под каждым методом имеют Intrinsic атрибут, который говорит что вызов метода надо подменить опкодом на этапе JIT компиляции. Грубо говоря, целый метод превратиться в одну инструкцию. В вашем случае, исходя из того, что написано в статье, нет ни одного атрибута. Конечно, к соответственной оптимизации вам не получится прийти, ибо всё это делается в исходниках CLR. Я вам предлагаю добавить MethodImpl атрибут с параметром AggressiveInlining, чтобы исключить ненужные прыжки по стеклу

Я вам предлагаю добавить MethodImpl атрибут с параметром AggressiveInlining, чтобы исключить ненужные прыжки по стеклу

Этого почти никогда не нужно делать (и он даже не всегда работает). JIT обычно сам может лучше определить когда нужно заинлайнить функцию, даже если этот атрибут не указан.

Sign up to leave a comment.

Articles