我的密码

double to_radians(double theta)
{
    return (M_PI * theta) / 180.0;
}

int main()
{
    std::vector<std::pair<double, double>> points;
    for (double theta = 0.0; theta <= 360.0; theta += 30.0)
    {
        points.push_back(std::make_pair(std::cos(to_radians(theta)), std::sin(to_radians(theta))));
    }
    for (auto point : points)
        std::cout << point.first << " " << point.second << "\n";
}

我期望的输出

1 0
0.866025 0.5
0.5 0.866025
0 1
-0.5 0.866025
-0.866025 0.5
-1 0
-0.866025 -0.5
-0.5 -0.866025
0 -1
0.5 -0.866025
0.866025 -0.5
1 0

我得到的输出:

1 0
0.866025 0.5
0.5 0.866025
6.12303e-17 1
-0.5 0.866025
-0.866025 0.5
-1 1.22461e-16
-0.866025 -0.5
-0.5 -0.866025
-1.83691e-16 -1
0.5 -0.866025
0.866025 -0.5
1 -2.44921e-16

如您所见,我得到的是这些奇怪的值,而不是零。有人可以解释为什么会这样吗?

最佳答案

6.12303e-17为例,它表示值6.12303 * 10-17或0.00000000000000000612303。

获得此值作为结果的原因是您没有将cos应用于π/ 2,无论如何它都不能表示为double(这是不合理的)。 cos函数应用于接近π/ 2的double,通过将90乘以M_PI并除以180获得。由于自变量不是π/ 2,因此结果不必为0。数字更接近于零,因此对于任何浮点格式,极不可能将正确舍入的cos应用于任何浮点数而产生的结果恰好为零。

实际上,由于cos在π/ 2中的导数为-1,因此为表达式cos(M_PI/2.0)获得的值非常接近M_PI/2与π/ 2之间的差。该差异确实约为d * 10-17,因为 double IEEE 754格式只能表示任意数字的前16个左右十进制数字。

注意,相同的参数适用于作为0.5的结果获得cos(M_PI/3.0),甚至作为-1.0的结果获得cos(M_PI)。区别在于,浮点数很多,有些非常小,大约为0,这些浮点数可以非常精确地表示预期的非零结果。相比之下,0.5-1.0只有几个邻居,并且对于足够接近π/ 3和π的输入,最终将数字0.5-1.0返回为与各自数学结果最接近的可表示 double 值(即t 1/2或-1,因为输入不是π/ 3或π)。

解决问题的最简单方法是使用假设函数cosdegsindeg,它们可以直接计算 Angular 余弦和正弦值。由于60和90可以精确表示为 double 浮点数,因此这些函数没有理由不返回0.5或0.0(也可以精确表示为 double 浮点数)。我之前问过a question in relation to these functions,但没有人指出任何已经可用的实现。

njuffa指出的函数sinpicospi经常可用,它们允许计算正弦和余弦或π/ 2,π/ 4甚至7.5 *π,但不能计算π/ 3,因为它们的数字为1/3在二进制浮点数中不能精确表示的将被应用。

09-09 20:02