hello i am writing basic calculator. everytime i use 90 degrees on cos function it gives -0 as result
int deg;
float rad,result;
printf("\ndegree\n");
scanf("%d",°);
rad = deg * (M_PI/180);
result=cos(rad);
printf("\nresult= %f",result);
i dont even know what to try. i just googled it and did not see any similar results.
CodePudding user response:
M_PI is defined as 3.141593...
which is slightly over PI, consequently, the cos(90.xxx) is lesser than 0.
If you try with 3.1415926
, you will get positive result:
https://onlinegdb.com/7MWNEkkqI
None of those two values match the real PI value, and they might even be defined differently on different compilers. The point is that having one above the real PI and the other below the real PI make them to fall in different quadrants, and a different sign on the result.
The float
being represented by 32bit, it is not possible to represent exactly most of the real numbers (except those few ~2^32 values). And going to double
will not solve this.
At the end, it is the function converting the number to a string for representation on the screen who can detect that "-0" and write "0" instead. That is why if you open most applications, you don't get "-0" very often.
The solution is to have the "print" (note, that this is not necessarily the official printf
) which is aware of the number of relevant bits, and can convert this -0.0000000x to 0; 0.9999999x to 1, etc.. Modern "print" functions will provide a mechanism to set the precision (for example std::setprecision
in C ).
Note: rounding the value will not work with very big or small numbers.
CodePudding user response:
Other answers have suggested changing the value of pi slightly.
Other answers have suggested changing type float
to type double
.
Both of these suggestions move the problem around slightly, perhaps changing the objectionable displayed value of -0 to plain 0.
(And switching from float
to double
is almost always a good idea, no matter what.)
But none of these suggestions actually "solve" this particular "problem", because fundamentally there is no actual problem here.
The fundamental problem, as I said in a comment, is that it is just not possible to compute the cosine of 90.0000000000 degrees, because you are never going to be able to represent the value π/2 perfectly accurately in radians. You're inevitably always going to be working with the equivalent of 89.9999999999 degrees, or 90.0000000001 degrees, so to speak. And when the cosine ends up being -0.0000000001, a high-quality version of printf
is going to round and display it as -0, because -0 is a thing in computer floating point.
If you have a "fixed" version of the original program that no longer displays cos(90) as -0, I suggest trying it with cos(-90), or cos(270) — I bet one or the other of those will display -0, so you're right back out of the frying pan and into the fire again.
If you have a requirement that says you must never display -0, I believe you'd want to pursue a completely different approach, probably along the lines of
char tmp[50];
snprintf(tmp, sizeof(tmp), "%f", result);
if(*tmp == '-' && atof(tmp) == 0) result = -result;
printf("result= %f\n", result);
CodePudding user response:
If you use %g
instead of %f
, you would see that the result is not exactly 0, but a very small, negative value. Hence the minus sign with %f
.
Now, for a more accurate result, you should use the type double
instead of float
for the variables rad
and result
(cos
already takes a double
and returns a double
). The sign will be positive, but the result will still not be 0 exactly. As π/2 is irrational, there is no way to get an exact 0 with the cos
function (unless its implementation is buggy).
The next C standard (C23) will include a cospi
function (as recommended by the IEEE 754 standard), which could solve your issue as it is defined as cospi(x) = cos(πx). So, for 90 degrees, you would call cospi
with the argument 0.5, which is exactly representable.
EDIT: Some implementations may be tempted to hide the issue by guessing what the result should be, such as assuming that if the cos
argument is very close to π/2, then it is regarded as π/2 exactly, so that an exact 0 is returned. This is a bad idea (in particular for generic libraries like the C library), which could yield surprising results. Even user code should be careful. See this video to see possible consequences of such hacks on a Casio calculator.
CodePudding user response:
cos(90) is exactly 0. Purely mathematically, 0 is equal to -0. I assume that the problem is the minus sign?
M_PI is of type double, with the value 3.14159265358979323846, which is slightly less than true pi.
Converting it to a float makes it 3.1415927410125732421875, which is slightly more than true pi. The calculations could be done in type double, and converted to float afterwards? That code would of course be slightly less efficient, but depending on the use case, it would probably not matter, and the risk for any similar errors would be minimized.
EDIT: I just realized M_PI exists as a float too, depending on the library, as previous answer states. Both ways should solve the problem.