将cos和sin存储到向量中并获得奇怪的值

时间:2014-12-25 00:16:15

标签: c++ floating-point

我的代码

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

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

2 个答案:

答案 0 :(得分:10)

举例来说,

6.12303e-17表示值6.12303 * 10 -17 或0.00000000000000000612303。

您获得此值的原因是您没有将cos应用于π/ 2,这无论如何都不能表示为double(这是不合理的)。 cos函数应用于接近π/ 2的double,通过将90乘以M_PI并除以180得到。由于参数不是π/ 2,结果不会事实上,由于浮点数在零附近更密集,因此对于任何浮点格式来说,将正确舍入的cos应用于任何浮点数的结果几乎不会产生零结果

事实上,由于π/ 2中cos的导数为-1,因此表达式cos(M_PI/2.0)获得的值与M_PI/2和π/之间的差值非常近似。 2。这个差异确实是d * 10 -17 的顺序,因为双精度IEEE 754格式只能代表任意数字的前16个左右第一个十进制数字。


请注意,相同的参数适用于0.5的结果,cos(M_PI/3.0)-1.0的{​​{1}}结果。不同之处在于有许多浮点数,有些非常小,大约为0,这些可以非常精确地表示预期的非零结果。相比之下,cos(M_PI)0.5只有几个邻居,而对于足够接近π/ 3和π的输入,数字-1.00.5最终会被返回为相应数学结果的最近可表示的双精度值(不是1/2或-1,因为输入不是π/ 3或π)。

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

njuffa指出的函数sindegsinpi通常是可用的,它们允许计算正弦和余弦或π/ 2,π/ 4甚至7.5 *π,但不能π/ 3,因为它们必须应用的数字1/3不能完全用二进制浮点表示。

答案 1 :(得分:0)

这是一个浮点舍入错误。 Trig函数实现为数学序列,它们在计算级别上近似,导致数字非常接近零,例如6.12303e-17而不是预期的0