Reputation: 19
My code
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";
}
Output I expect
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
Output I get:
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
As you can see I am getting these strange values instead of zero. Can somebody explain why this is happening?
Upvotes: 1
Views: 627
Reputation: 80255
6.12303e-17
, to take an example, represents the value 6.12303*10-17, or 0.00000000000000000612303.
The reason you obtain this value as result is that you did not apply cos
to π/2, which is not representable as a double
anyway (it's irrational). The cos
function was applied to a double
close to π/2, obtained by multiplying 90 by M_PI
and dividing by 180. Since the argument is not π/2, the result does not have to be 0. In fact, since floating-point numbers are more dense near zero, it is extremely unlikely for any floating-point format that applying a correctly rounded cos
to any floating-point number produces exactly zero as result.
In fact, since the derivative of cos
in π/2 is -1, the value obtained for the expression cos(M_PI/2.0)
is a close approximation of the difference between M_PI/2
and π/2. That difference is indeed of the order of d*10-17, since the double-precision IEEE 754 format can only represent the first 16 or so first decimal digits of an arbitrary number.
Note that the same argument applies to obtaining 0.5
as the result of cos(M_PI/3.0)
, or even -1.0
as the result of cos(M_PI)
. The difference is that there are many floating-point numbers, some very small, around 0, and these can represent very precisely the intended non-zero result. In comparison, 0.5
and -1.0
have only a few neighbors, and for inputs close enough to π/3 and π, the numbers 0.5
and -1.0
end up being returned as the nearest representable double-precision value to the respective mathematical result (which isn't 1/2 or -1, since the input is not π/3 or π).
The simplest solution to your problem would be to use hypothetical functions cosdeg
and sindeg
that would compute directly the cosine and sine of angles in degrees. Since 60 and 90 are representable exactly as double-precision floating-point numbers, these functions would have no excuse not to return 0.5 or 0.0 (also exactly representable as double-precision floating-point numbers). I asked a question in relation to these functions earlier but no-one pointed to any already available implementation.
The functions sinpi
and cospi
pointed out by njuffa are often available, and they allow to compute the sine and cosine or π/2, π/4 or even 7.5*π, but not of π/3, since the number 1/3 they would have to be applied to is not representable exactly in binary floating-point.
Upvotes: 10
Reputation: 5064
It's a floating point rounding error. Trig functions are implemented as mathematical series that are approximated on the computational level which causes for numbers very close to zero for example 6.12303e-17
rather than the expected 0
.
Upvotes: 0