c/c++ 实现三角函数(不使用库函数) sin/cos/tan/cot

简介

在许多嵌入式系统或计算平台中,可能不方便使用标准数学库函数。在本篇博客中,我们将使用 泰勒展开 来实现常见的三角函数:sin(x)、cos(x)、tan(x) 和 cot(x),而不依赖任何库函数。通过手动实现这些数学公式,我们可以更好地理解三角函数的计算原理,同时为那些受限的环境提供一种实现方案。

泰勒展开简介

泰勒展开是数学中一个非常重要的公式,它能够将任何在某点可导的函数展开为一个无限级数。对于三角函数,我们可以利用其在0点的泰勒级数展开来近似计算函数值。以 sin(x) 为例,它的泰勒展开式为:
s i n ( x ) = x − x 3 3 ! + x 5 5 ! − x 7 7 ! + . . . sin(x)=x-\frac {x^3}{3!} + \frac {x^5}{5!} - \frac {x^7}{7!} + ... sin(x)=x3!x3+5!x57!x7+...

这意味着通过不断累加各阶项,可以近似计算出 s i n ( x ) sin(x) sin(x) 的值。每增加一项,结果会更加精确。

收敛性:
泰勒级数在某些情况下会收敛得非常快(如 x 接近 0 时)。然而,当 x 值较大时,收敛速度会减慢,需要更多项才能达到理想的精度。因此,在实现时我们设置了精度阈值 1e-15,当项的绝对值小于该值时,停止计算。

代码实现

#include <iostream>
#include <cstdlib>
#include <iomanip>

constexpr auto PI = 3.1415926;

// 显示菜单并选择操作
int menu_select() {
    int select;
    std::cout << "-------Welcome to Snow Calculator-------" << std::endl;
    std::cout << "               1. sin x                 " << std::endl;
    std::cout << "               2. cos x                 " << std::endl;
    std::cout << "               3. tan x                 " << std::endl;
    std::cout << "               4. cot x                 " << std::endl;
    std::cout << "               5. Clean screen          " << std::endl;
    std::cout << "               0. to quit               " << std::endl;
    std::cout << "----------------------------------------" << std::endl;
    std::cin >> select;
    return select;
}

// 输入角度,并将角度转换为弧度
double inputtrans(void) {
    double x;
    while (true) {
        std::cout << "Please input x (angle in degrees): ";
        std::cin >> x;
        if (std::cin.fail()) { // 输入非法时
            std::cin.clear();  // 清除输入错误标志
            std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
            std::cout << "Invalid input. Please try again." << std::endl;
            continue;
        }
        if (x >= 360) {
            x -= 360.0;  // 把角度归一化
        }
        break;
    }
    return (x * PI / 180.0); // 将角度转换为弧度
}

// 计算阶乘(迭代版本,避免递归栈溢出)
double factorial(int n) {
    double result = 1;
    for (int i = 1; i <= n; ++i) {
        result *= i;
    }
    return result;
}

// 计算绝对值
double myabs(double num1) {
    return((num1 > 0) ? num1 : -num1);
}

// 使用泰勒展开计算sin(x)
double snowsin(double x) {
    double result = 0.0;
    double term = x;  // 初始化为 x^1 / 1!
    int n = 1;
    while (myabs(term) > 1e-15) { // 直到当前项小于精度要求
        result += term;
        term = -term * x * x / ((2 * n) * (2 * n + 1)); // 更新项
        ++n;
    }
    return result;
}

// 使用泰勒展开计算cos(x),通过 sin(PI/2 - x) 来优化
double snowcos(double x) {
    x = (PI / 2) - x;  // cos(x) = sin(PI/2 - x)
    return snowsin(x);
}

// 使用sin(x) 和 cos(x) 计算tan(x)
double snowtan(double x) {
    return (snowsin(x) / snowcos(x));
}

// 使用tan(x) 计算cot(x)
double snowcot(double x) {
    return (1 / snowtan(x));
}

int main() {
    double result = 0.0;
    double x = 0.0;
    int menu = menu_select();
    while (menu != 0) {
        switch (menu) {
        case 1:
            x = inputtrans();
            result = snowsin(x);
            std::cout << std::fixed << std::setprecision(5) << "Result =  " << result << std::endl;
            break;
        case 2:
            x = inputtrans();
            result = snowcos(x);
            std::cout << std::fixed << std::setprecision(5) << "Result =  " << result << std::endl;
            break;
        case 3:
            x = inputtrans();
            result = snowtan(x);
            std::cout << std::fixed << std::setprecision(5) << "Result =  " << result << std::endl;
            break;
        case 4:
            x = inputtrans();
            result = snowcot(x);
            std::cout << std::fixed << std::setprecision(5) << "Result =  " << result << std::endl;
            break;
        case 5:
            system("cls");
            break;
        case 0:
            break;
        default:
            std::cout << "Wrong input, please input again!" << std::endl;
            break;
        }
        if (menu == 0) {
            break;
        }
        menu = menu_select();
    }

    std::cout << "Thank you for using!\nPress any key to exit.\n";
    system("pause");
}

代码解释

  • 输入和输出: 用户输入角度(以度为单位),程序会将角度转换为弧度并计算相应的三角函数值。
  • 泰勒展开: 使用泰勒展开计算 sin(x)、cos(x)、tan(x) 和 cot(x)。对于 sin(x) 和 cos(x),通过反复累加展开项,直到当前项的绝对值小于 1e-15 时停止计算。
  • 阶乘计算: 使用迭代方式计算阶乘,避免递归深度过深导致栈溢出。

常见问题与改进建议

  1. 精度问题:
    由于使用的是泰勒展开,随着计算项数的增加,计算的精度会逐渐提高,但对较大角度的计算可能存在一定误差。可以通过将角度 x x x转换到 [ − π , π ] [-π, π] [π,π]范围内来提高精度。

  2. 优化:
    当前的snowsinsnowcos计算方式较为低效,可以通过利用一些三角恒等式来优化。例如:
    c o s ( x ) = s i n ( π 2 − x ) cos(x)=sin(\frac{π}{2}-x) cos(x)=sin(2πx)
    这种优化可以避免不必要的计算,提高程序效率。

总结

通过不使用标准库,直接使用泰勒展开来实现三角函数的计算,不仅加深了对数学公式的理解,也为特定需求下的低资源环境提供了一种有效的计算方法。尽管该方法在某些情况下可能精度不高,但在学习和嵌入式计算中仍然具有很高的价值。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐