sin в C/C++: вычисление синуса

👋 Привет! В этой статье мы разберем функцию sin в C/C++. Эта функция вычисляет синус угла. Начнем мы с рассмотрения примера использования этой функции, а потом попробуем реализовать ее самостоятельно. В конце статьи вы найдете упражнения для закрепления материала.

Иллюстрация синуса

Как вычислить синус в C++

Для вычисления синуса в C++ мы можем воспользоваться функцией sin. Эта функция объявлена в заголовочном файле <math.h>. В C++ вы можете подключать <cmath>. Выглядит эта функция следующим образом:

double sin (double x);
  • Первым аргументом функция принимает вещественное число. Тут мы передаем угол в радианах, для которого нужно посчитать синус.
  • Функция возвращает синус переданного ей угла.

Давайте посмотрим на пример использования этой функции:

#include <cmath>
#include <iostream>

using namespace std;

int main() {
  cout << "sin(M_PI * 1 / 2) = " << sin(M_PI * 1 / 2) << endl;
  cout << "sin(M_PI * 2 / 3) = " << sin(M_PI * 2 / 3) << endl;
  cout << "sin(M_PI) = " << sin(M_PI) << endl;
  cout << "sin(0) = " << sin(0) << endl;
  cout << "sin(2 * M_PI) = " << sin(2 * M_PI) << endl;

  return 0;
}

Константа M_PI доступна после подключения библиотеки <cmath>. Это число примерно равно 3.1415.

Вывод программы:

sin(M_PI * 1 / 3) = 1
sin(M_PI * 2 / 3) = 0.866025
sin(M_PI) = 1.22465e-16
sin(0) = 0
sin(2 * M_PI) = -2.44929e-16

Для чисел M_PI и 2 * M_PI мы получили значения очень близкие к нулю, но не идеальный ноль. Это связано с тем, что точность константы M_PI не бесконечна, в отличии от π в математике.

Что будет выведено на экран?

#include <cmath>
#include <iostream>

using namespace std;

int main() {
  cout << "sin(2*M_PI + M_PI / 2) = " << sin(2*M_PI + M_PI / 2) << endl;

  return 0;
}
0.5
1
-0.5
-1

Как реализовать sin самому

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

Ряд Тейлора для sin

Давайте реализуем функцию, которая будет подсчитывать этот ряд:

#include <cmath>
#include <iostream>

using namespace std;

// в этой функции мы будем считать факториал числа рекурсивно
int factorial(int n) {
  if (n == 0) {
    return 1;
  }

  return n * factorial(n - 1);
}

// подсчет степени числа
double pow(double base, int exponent) {
  double result = 1;
  for (int i = 0; i < exponent; i++) {
    result *= base;
  }

  return result;
}

// нам нужно будет знать знак числа
double sign(double x) {
  if (x > 0) {
    return 1;
  } else if (x < 0) {
    return -1;
  } else {
    return 0;
  }
}

// подсчет синуса через ряд Тейлора выше
double my_sin(double x) {
  // наша реализация работает только для чисел от -2*M_PI до 2*M_PI
  // поэтому мы пользуемся тем, что sin(x) == sin(x + 2*M_PI) для любого x
  while (fabs(x) >= 2 * M_PI) {
    x -= sign(x) * 2 * M_PI;
  }

  double result = x;
  // тут мы подсчитываем 4 элементов ряда
  // можно подсчитывать больше для лучшей точности
  for (int i = 1; i <= 4; i++) {
    double term = (double)pow(x, 1 + 2 * i) / factorial(1 + 2 * i);
    if (i % 2 == 1) {
      result -= term;
    } else {
      result += term;
    }
  }
  return result;
}

int main() {
  cout << "my_sin(M_PI * 1 / 2) = " << my_sin(M_PI * 1 / 2) << endl;
  cout << "my_sin(M_PI * 2 / 3) = " << my_sin(M_PI * 2 / 3) << endl;
  cout << "my_sin(M_PI) = " << my_sin(M_PI) << endl;
  cout << "my_sin(0) = " << my_sin(0) << endl;
  cout << "my_sin(2 * M_PI) = " << my_sin(2 * M_PI) << endl;
  return 0;
}

Вывод этой программы:

my_sin(M_PI * 1 / 2) = 1
my_sin(M_PI * 2 / 3) = 0.866108
my_sin(M_PI) = 0.00692527
my_sin(0) = 0
my_sin(2 * M_PI) = 0

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

Функции sinl и sinf

Функции sinl и sinf работают как и обычный sin, только с другими типами. Давайте посмотрим на прототип функции sinl:

long double sinl (long double x);

Как можете видеть, функция sinl принимает и возвращает long double (sin принимает и возвращает просто double). Теперь давайте посмотрим на sinf:

float sinf (float x);

Тут мы уже видим float. Давайте попробуем сравнить результаты этих функций:

cout << "sinf(M_PI) = " << sinf(M_PI) << endl;
cout << "sin (M_PI) = " << sin(M_PI) << endl;
cout << "sinl(M_PI) = " << sinl(M_PI) << endl;

Вывод программы:

sinf(M_PI) = -8.74228e-08
sin (M_PI) = 1.22465e-16
sinl(M_PI) = 1.22465e-16

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

Упражнения

  1. Использование sin:
    Напишите программу на C++, которая запрашивает у пользователя угол в радианах, вычисляет его синус с помощью функции sin и выводит результат. Также программа должна выводить введенный пользователем угол.

  2. Собственная реализация sin:
    Используя ряд Тейлора из статьи, создайте свою функцию для вычисления синуса. Проверьте её работу на различных углах и сравните с результатами стандартной функции sin.

  3. Сравнение точности:
    Напишите программу, которая сравнивает результаты вычисления синуса с использованием стандартной функции sin и вашей собственной реализации. Выведите разницу между ними для различных углов.

Обсуждение