我的密码
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或π)。
解决问题的最简单方法是使用假设函数cosdeg
和sindeg
,它们可以直接计算 Angular 余弦和正弦值。由于60和90可以精确表示为 double 浮点数,因此这些函数没有理由不返回0.5或0.0(也可以精确表示为 double 浮点数)。我之前问过a question in relation to these functions,但没有人指出任何已经可用的实现。
njuffa指出的函数sinpi
和cospi
经常可用,它们允许计算正弦和余弦或π/ 2,π/ 4甚至7.5 *π,但不能计算π/ 3,因为它们的数字为1/3在二进制浮点数中不能精确表示的将被应用。