.NET включает базовые тригонометрические функции (sin, cos, tan, asin, acos, atan) и их гиперболические аналоги (sinh, cosh, tanh, asinh, acosh, atanh) в классе Math. Однако в .NET отсутствуют следующие тригонометрические функции:

  • Sec (секанс)

  • Csc (косеканс)

  • Cot (котангенс)

  • Asec (обратный секанс)

  • Acsc (обратный косеканс)

  • Acot (обратный котангенс)

  • Sech (гиперболический секанс)

  • Csch (гиперболический косеканс)

  • Coth (гиперболический котангенс)

  • Asech (обратный гиперболический секанс)

  • Acsch (обратный гиперболический косеканс)

  • Acoth (обратный гиперболический котангенс)

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

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

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

Зачем использовать эту библиотеку?

  1. Функция арккотангенса (arccot)

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

Предпочтительный диапазон для arccot(x) составляет (0, π), а не (−π/2, π/2).
Предпочтительный диапазон для arccot(x) составляет (0, π), а не (−π/2, π/2).

С математической точки зрения, функция арккотангенса arccot(x) обычно определяется как угол, чей котангенс равен x. Предпочтительный диапазон для arccot(x) составляет (0, π), а не (−π/2, π/2), так как он сохраняет согласованность с другими обратными тригонометрическими функциями. Вот реализация функции arccot с использованием существующей функции 'Math.Atan' в C#:

public static double Acot(double d)
{
    return Math.Atan(1.0 / d);
}

Эта реализация не учитывает правильный диапазон функции арккотангенса, что приводит к неточным результатам для отрицательных входных значений. Вот правильная реализация, применяющая свойство симметрии arccot(−x) = π − arccot(x) для отрицательных входных значений:

public static double Acot(double d)
{
    //the Trigonometric Symmetry is applied: arccot(−x) = π − arccot(x)
    if (IsNegative(d))
        return Math.PI - Math.Atan(1 / -d);

    return Math.Atan(1.0 / d);
}

Математики предпочитают правильную реализацию, так как она придерживается стандартного диапазона (0, π), обеспечивая согласованность и избегая неоднозначностей в расчетах.

  1. Повышение точности

Функция обратного гиперболического косеканса определяется как:

Arcsch(x) = ln[1/x + √(1/(x^2) + 1)]

При вычислении этой функции для двух противоположных входных значений, таких как -0.1E-7 и 0.1E-7, получаем 19.1138 для x = 0.1E-7 и -∞ для x = -0.1E-7.

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

Чтобы уменьшить эти ошибки, можно использовать свойство симметрии функции обратного гиперболического косеканса: arcsch(−x) = −arcsch(x). Применяя это свойство для отрицательных входных значений, можно избежать вычислений значений, близких к нулю, и тем самым повысить точность результатов.

Пример:

public static double Acsch(double x)
{
    if (x == 0.0)
        return double.NaN;

    //the Trigonometric Symmetry is applied: arcsch(−x)=−arcsch(x)
    if (IsNegative(x))
        return -Math.Log(1.0 / -x + Math.Sqrt(1.0 / (x * x) + 1.0));

    return Math.Log(1.0 / x + Math.Sqrt(1.0 / (x * x) + 1.0));
}
  1. Обеспечение диапазонов входных и выходных значений

Тригонометрические функции часто требуют специфических диапазонов входных значений для получения допустимых результатов. Например, функция обратного гиперболического секанса arsech(x) принимает значения только в диапазоне (0, 1]. При реализации пользовательских тригонометрических функций важно проверять диапазоны входных значений, чтобы предотвратить ошибки выполнения и обеспечить точные вычисления.

Пример:

public static double Asech(double x)
{
    if (x is <= 0.0 or > 1.0)
        return double.NaN;

    return Math.Log(1.0 / x + Math.Sqrt(1.0 / (x * x) - 1.0));
}

Заключение

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

Чтобы установить библиотеку, используйте следующую команду в консоли NuGet Package Manager:

Install-Package MathTrigonometric

Я также работаю над библиотекой .NET для быстрого вычисления сложных научных математических формул, что значительно улучшит возможности C#. Вы можете поддержать мои усилия, став спонсором на GitHub. Ваши взносы помогут развитию open-source и улучшению инструментов, доступных для сообщества.

Спасибо! Пожалуйста, оставляйте идеи в комментариях!

Комментарии (17)


  1. censor2005
    18.06.2024 15:26
    +3

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


  1. withkittens
    18.06.2024 15:26
    +4

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

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

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


    1. AntonAntonov88 Автор
      18.06.2024 15:26
      +2

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


  1. aamonster
    18.06.2024 15:26
    +2

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


    1. AntonAntonov88 Автор
      18.06.2024 15:26
      +1

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


      1. aamonster
        18.06.2024 15:26

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


  1. Refridgerator
    18.06.2024 15:26
    +10

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

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


  1. impwx
    18.06.2024 15:26
    +1

    Это все уже давно реализовано в MathNet.Numerics.


    1. AntonAntonov88 Автор
      18.06.2024 15:26
      +1

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


      1. Refridgerator
        18.06.2024 15:26
        +1

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


  1. markshevchenko
    18.06.2024 15:26
    +7

    Здесь есть проблема в коде. В .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.


    1. AntonAntonov88 Автор
      18.06.2024 15:26

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


  1. Refridgerator
    18.06.2024 15:26

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


    1. AntonAntonov88 Автор
      18.06.2024 15:26

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


      1. Refridgerator
        18.06.2024 15:26
        +2

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


  1. KvanTTT
    18.06.2024 15:26
    +3

    Math.Pow(x, 2.0)

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


    1. AntonAntonov88 Автор
      18.06.2024 15:26

      Да, вы правы