weird result when cos is 0 - c

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",&deg);
rad = deg * (M_PI/180);
result=cos(rad);
printf("\nresult= %f",result);
result
i dont even know what to try.
i just googled it and did not see any similar results.

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.

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 real issue, as I said in a comment, is that it is just not possible to compute the cosine of 90.0000000000 degrees, at all, 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. That is, the problem isn't that cos() is computing the wrong value; the problem is that you're not even passing it the "right" value to begin with! And when π/2 comes out a little bit over, meaning that cos() ends up computing a value like -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 would want to pursue a completely different approach, perhaps something 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);
It may seem strange to be tinkering around with the string representation like this, but it actually makes sense, because it's only after converting (and perhaps rounding) to a string representation that we can be sure we've got the "-0.000000" case that needs special attention.

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.

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.

everytime i use 90 degrees on cos function it gives -0 as result
Rounded text
The result is not -0 but a value between 0.0 and -0.0000005 that when printed using "%f" (in the form d.dddddd) OP saw a rounded value as "-0.000000".
To see a more informative output, use "%g".
printf("\nresult= %g",result);
// result= -4.37114e-08
Related graphic
Why -4.37114e-08?
rad = deg * (M_PI/180); attempts to convert degrees to radians using an approximation to π. π is irrational. All finite floating point values are rational and so M_PI is never exactly π, regardless how precise the floating point type. Thus rad = deg * (M_PI/180); introduces small errors that are magnified in cos(rad) when it performs it argument range reduction.
There is an alternative. Perform the argument range reduction in degrees, which can be exact, scale and then call cos().
#include <math.h>
#include <stdio.h>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
static double d2r(double d) {
return (d / 180.0) * ((double) M_PI);
}
double cosd(double x) {
if (!isfinite(x)) {
return cos(x);
}
int quo;
double d_45 = remquo(fabs(x), 90.0, &quo);
// d_45 is in the range [-45...45]
double r_pidiv4 = d2r(d_45);
switch (quo % 4) {
case 0:
return cos(r_pidiv4);
case 1:
// Add 0.0 to avoid -0.0
return 0.0 - sin(r_pidiv4);
case 2:
return -cos(r_pidiv4);
case 3:
return sin(r_pidiv4);
}
return 0.0;
}
Test
int main(void) {
int prec = DBL_DECIMAL_DIG - 1;
for (int d = -360; d <= 360; d += 15) {
double r = d2r(d);
printf("cos (%6.1f degrees) = % .*e\n", 1.0 * d, prec, cos(r));
printf("cosd(%6.1f degrees) = % .*e\n", 1.0 * d, prec, cosd(d));
}
return 0;
}
Ouput
cos (-360.0 degrees) = 1.0000000000000000e+00
cosd(-360.0 degrees) = 1.0000000000000000e+00
...
cos (-270.0 degrees) = -1.8369701987210297e-16
cosd(-270.0 degrees) = 0.0000000000000000e+00 // Exactly zero
...
cos ( 0.0 degrees) = 1.0000000000000000e+00
cosd( 0.0 degrees) = 1.0000000000000000e+00
...
cos ( 60.0 degrees) = 5.0000000000000011e-01 // Not 0.5
cosd( 60.0 degrees) = 4.9999999999999994e-01 // Not 0.5, yet closer
...
cos ( 90.0 degrees) = 6.1232339957367660e-17
cosd( 90.0 degrees) = 0.0000000000000000e+00 // Exactly zero, OP's goal
...
cos ( 270.0 degrees) = -1.8369701987210297e-16
cosd( 270.0 degrees) = 0.0000000000000000e+00 // Exactly zero
...
cos ( 360.0 degrees) = 1.0000000000000000e+00
cosd( 360.0 degrees) = 1.0000000000000000e+00

Related

Underflow error in floating point arithmetic in C

I am new to C, and my task is to create a function
f(x) = sqrt[(x^2)+1]-1
that can handle very large numbers and very small numbers. I am submitting my script on an online interface that checks my answers.
For very large numbers I simplify the expression to:
f(x) = x-1
By just using the highest power. This was the correct answer.
The same logic does not work for smaller numbers. For small numbers (on the order of 1e-7), they are very quickly truncated to zero, even before they are squared. I suspect that this has to do with floating point precision in C. In my textbook, it says that the float type has smallest possible value of 1.17549e-38, with 6 digit precision. So although 1e-7 is much larger than 1.17e-38, it has a higher precision, and is therefore rounded to zero. This is my guess, correct me if I'm wrong.
As a solution, I am thinking that I should convert x to a long double when x < 1e-6. However when I do this, I still get the same error. Any ideas? Let me know if I can clarify. Code below:
#include <math.h>
#include <stdio.h>
double feval(double x) {
/* Insert your code here */
if (x > 1e299)
{;
return x-1;
}
if (x < 1e-6)
{
long double g;
g = x;
printf("x = %Lf\n", g);
long double a;
a = pow(x,2);
printf("x squared = %Lf\n", a);
return sqrt(g*g+1.)- 1.;
}
else
{
printf("x = %f\n", x);
printf("Used third \n");
return sqrt(pow(x,2)+1.)-1;
}
}
int main(void)
{
double x;
printf("Input: ");
scanf("%lf", &x);
double b;
b = feval(x);
printf("%f\n", b);
return 0;
}
For small inputs, you're getting truncation error when you do 1+x^2. If x=1e-7f, x*x will happily fit into a 32 bit floating point number (with a little bit of error due to the fact that 1e-7 does not have an exact floating point representation, but x*x will be so much smaller than 1 that floating point precision will not be sufficient to represent 1+x*x.
It would be more appropriate to do a Taylor expansion of sqrt(1+x^2), which to lowest order would be
sqrt(1+x^2) = 1 + 0.5*x^2 + O(x^4)
Then, you could write your result as
sqrt(1+x^2)-1 = 0.5*x^2 + O(x^4),
avoiding the scenario where you add a very small number to 1.
As a side note, you should not use pow for integer powers. For x^2, you should just do x*x. Arbitrary integer powers are a little trickier to do efficiently; the GNU scientific library for example has a function for efficiently computing arbitrary integer powers.
There are two issues here when implementing this in the naive way: Overflow or underflow in intermediate computation when computing x * x, and substractive cancellation during final subtraction of 1. The second issue is an accuracy issue.
ISO C has a standard math function hypot (x, y) that performs the computation sqrt (x * x + y * y) accurately while avoiding underflow and overflow in intermediate computation. A common approach to fix issues with subtractive cancellation is to transform the computation algebraically such that it is transformed into multiplications and / or divisions.
Combining these two fixes leads to the following implementation for float argument. It has an error of less than 3 ulps across all possible inputs according to my testing.
/* Compute sqrt(x*x+1)-1 accurately and without spurious overflow or underflow */
float func (float x)
{
return (x / (1.0f + hypotf (x, 1.0f))) * x;
}
A trick that is often useful in these cases is based on the identity
(a+1)*(a-1) = a*a-1
In this case
sqrt(x*x+1)-1 = (sqrt(x*x+1)-1)*(sqrt(x*x+1)+1)
/(sqrt(x*x+1)+1)
= (x*x+1-1) / (sqrt(x*x+1)+1)
= x*x/(sqrt(x*x+1)+1)
The last formula can be used as an implementation. For vwry small x sqrt(x*x+1)+1 will be close to 2 (for small enough x it will be 2) but we don;t loose precision in evaluating it.
The problem isn't with running into the minimum value, but with the precision.
As you said yourself, float on your machine has about 7 digits of precision. So let's take x = 1e-7, so that x^2 = 1e-14. That's still well within the range of float, no problems there. But now add 1. The exact answer would be 1.00000000000001. But if we only have 7 digits of precision, this gets rounded to 1.0000000, i.e. exactly 1. So you end up computing sqrt(1.0)-1 which is exactly 0.
One approach would be to use the linear approximation of sqrt around x=1 that sqrt(x) ~ 1+0.5*(x-1). That would lead to the approximation f(x) ~ 0.5*x^2.

Issue with asin() while calculating pi

I am trying to self teach myself C (C99 I think? gcc 8.1.0) coming from python/java. One of the practice problems I am working on is how to calculate pi to a given decimal.
I am currently using the following equation 2 * (Arcsin(sqrt(1 - 0.5^2)) + abs(Arcsin(0.5))).
float pi_find(float nth)
{
float x, y, z;
/* Equation = 2 * (Arcsin(sqrt(1 - x^2)) + abs(Arcsin(x))) [x|-1<=x=>1, xeR]*/
x = sqrt(1-pow(nth, 2)); /* Carrot (^) notation does not work, use pow() */
y = fabs(asin(nth)); /* abs is apparently int only, use fabs for floats */
z = x+y;
printf("x: %f\ny: %f\nsum: %f\n", x, y, (x+y));
printf("%f\n", asin(z));
return 2 * asin(z); /* <- Error Happens */
}
int main()
{
float nth = 0.5f;
double pi = pi_find(nth);
printf("Pi: %f\n", pi);
return 0;
}
Results:
x: 0.866025
y:0.523599
sum: 1.389624
z:-1.#IND00
Pi:-1.#IND00
I know the issue lies in the addition of x + y which sums out to 1.389... and asin() can only handle values between -1 and +1 inclusive.
HOWEVER!
I am using Wolfram Alpha along side python to check the calc is correct at every step and it can calculate asin(1.389...). [1]
I don't understand Imaginary mathematics, it is far beyond my capabilities as a mathematician but below is what Wolfram is doing. [2]
1.570796 -0.8563436 i
Interpreting as: 0.8563436 i
Assuming multiplication | Use a list instead
Assuming i is the imaginary unit | Use i as a variable instead
While writing this I found out about the _Imaginary Datatype added in C99, but I don't really understand if it's doing the same thing as what Wolfram does.
Also looked up how imaginary numbers worked, but I don't really understand how 'The square roots of a negative number cannot be distinguished until one of the two is defined as the imaginary unit' works. [3]
Can someone nudge me in the direction to fix this please?
It is obviously a knowledge issue and not a mathematical or language limitation
p.s yes I know it's trash code, I am using a weird way of debugging before I rewrite it properly.
[1]:Wolfram_Alpha Calculation
[2]:Wolfram_Alpha Assumption
[3]:Imaginary Numbers
The problem is you're grouping the expression incorrectly. The desired expression is:
2 * (Arcsin(sqrt(1 - 0.5^2)) + abs(Arcsin(0.5)))
With nth substituted for 0.5, this becomes:
2 * (Arcsin(sqrt(1 - nth^2)) + abs(Arcsin(nth))).
In particular, the argument to the first Arcsin is sqrt(1 - nth^2)), and the argument to the second Arcsin is nth.
You're also better off using nth * nth rather than pow(nth, 2). It's both faster and more accurate.
So what you want is:
x = asin(sqrt(1 - nth*nth));
y = fabs(asin(nth));
r = 2*(x + y);
Notice that the argument to asin can never have magnitude greater than 1 (as long as nth is less than 1).
Also, as I mentioned earlier in a comment, you should change all your float variables to double. You're using the double-precision math library functions anyway, so there's no reason to discard half of the precision by storing the results in float variables.
In C, the float and double types model "real" numbers, which I'll assume you have a handle on.
In mathematics, "complex" numbers are an extension of the real numbers. Every real number counts as a complex number, but so do "imaginary numbers", which you can get by multiplying the real numbers by the "imaginary unit" (labeled i in mathematical notation, and conventionally described as "the square root of -1").
Mathematically speaking, the basic arithmetic operations (+, -, *, /) are defined on complex numbers. It turns out that you can extend functions like arcsine to operate on complex numbers as well.
Without getting any further into the details, the Wolfram Alpha is almost certainly giving you values from a complex version of arcsine.
However, the standard C function asin() is the un-extended version: it takes a double as an argument, and returns a double as a result. Since double only models real numbers, asin() makes no sense for input values outside [-1,1].

Why does this code not do cube roots of negative numbers?

I was writing a function in C used to make roots of numbers, and I stumbled upon a problem: it works really well with cube roots of positive numbers and square roots, but when I try to make a cube root of a negative numer, it returns: -1.#IND00
I tried researching and it turns out the returned number is too big, but I can't understand why...
('rooter' is the function, x is the radicand and ind is the degree.)
I also tried to put in '0.66' instead of 1/ind but the same result happens.
float rooter(int x, int ind)
{
if(ind%2==0)
{
if (x>=0)
return ( pow(x, 1.0/ind) );
else
errore=1;
return -1;
}
else
{
return ( pow(x, (float)1.0/ind) );
}
}
pow does not accept negative base for non integer exponent. (Probably because making the special cases where it is traditionally defined work is too burdensome especially when it is expected to be implemented using logarithms.)
This answer is a little more philosophical than the other answers so far and attempts to address the underlying question "why doesn't pow allow a negative base with a float exponent?".
Consider how floating point numbers work and how powers to rational numbers are defined. Now given a negative base, ask yourself for what rational exponents is the result a real number?
Using the usual mathematical definition (-2)^(1/2) isn't a real number but you could find something arbitrarily close to 1/2 for which it is. For example (-2)^(49999/99999) is real. What this means is that if the implementation tried to determine what is and isn't real then any floating point precision error may actually swap your expression from real to imaginary, or vice versa, which would be unstable from a programmers perspective.
Another issue with this type of definition is that it requires us to represent the rational exponent in its most reduced form to determine if the expression is real or not. This isn't generally a trivial representation to determine. Note that (-2)^(2/4) is imaginary, even though the fourth root of -2 squared is real.
As stated by AProgrammer, the pow function does not accept negative x in your circumstances. To get round this, you can 'remember' the sign of x, pass its positive value (magnitude) to pow, then re-apply the sign (as you've already checked that ind is an odd number in this case):
else {
float sign = x < 0.0 ? -1.0 : +1.0;
// return sign * ( pow(fabs(x), (float)1.0/ind) ); // MNC - see comments
return sign * ( pow(fabs(x), 1.0/ind) ); // BPC - maybe?
}
Feel free to ask for further clarification and/or explanation.
pow would have to return complex numbers in order to handle negative bases without a much more complex API. Their approach is fast, easy to use and accessible. There is but one simple rule: the base must be zero or positive ^^
Plus, why the cast to float?
Simply change the code to
else
{
return pow(fabs(x), 1.0 / ind) * (x < 0 ? -1 : 1);
}

Taylor Series to calculate cosine (getting output -0.000 for cosine(90))

I have written the following function for the Taylor series to calculate cosine.
double cosine(int x) {
x %= 360; // make it less than 360
double rad = x * (PI / 180);
double cos = 0;
int n;
for(n = 0; n < TERMS; n++) {
cos += pow(-1, n) * pow(rad, 2 * n) / fact(2 * n);
}
return cos;
}
My issue is that when i input 90 i get the answer -0.000000. (why am i getting -0.000 instead of 0.000?)
Can anybody explain why and how i can solve this issue?
I think it's due to the precision of double.
Here is the main() :
int main(void){
int y;
//scanf("%d",&y);
y=90;
printf("sine(%d)= %lf\n",y, sine(y));
printf("cosine(%d)= %lf\n",y, cosine(y));
return 0;
}
It's totally expected that you will not be able to get exact zero outputs for cosine of anything with floating point, regardless of how good your approach to computing it is. This is fundamental to how floating point works.
The mathematical zeros of cosine are odd multiples of pi/2. Because pi is irrational, it's not exactly representable as a double (or any floating point form), and the difference between the nearest neighboring values that are representable is going to be at least pi/2 times DBL_EPSILON, roughly 3e-16 (or corresponding values for other floating point types). For some odd multiples of pi/2, you might "get lucky" and find that it's really close to one of the two neighbors, but on average you're going to find it's about 1e-16 away. So your input is already wrong by 1e-16 or so.
Now, cosine has slope +1 or -1 at its zeros, so the error in the output will be roughly proportional to the error in the input. But to get an exact zero, you'd need error smaller than the smallest representable nonzero double, which is around 2e-308. That's nearly 300 orders of magnitude smaller than the error in the input.
While you coudl in theory "get lucky" and have some multiple if pi/2 that's really really close to the nearest representable double, the likelihood of this, just modelling it as random, is astronomically small. I believe there are even proofs that there is no double x for which the correctly-rounded value of cos(x) is an exact zero. For single-precision (float) this can be determined easily by brute force; for double that's probably also doable but a big computation.
As to why printf is printing -0.000000, it's just that the default for %f is 6 places after the decimal point, which is nowhere near enough to see the first significant digit. Using %e or %g, optionally with a large precision modifier, would show you an approximation of the result you got that actually retains some significance and give you an idea whether your result is good.
My issue is that when i input 90 i get the answer -0.000000. (why am i getting -0.000 instead of 0.000?)
cosine(90) is not precise enough to result in a value of 0.0. Use printf("cosine(%d)= %le\n",y, cosine(y)); (note the e) to see a more informative view of the result. Instead, cosine(90) is generating a negative result in the range [-0.0005 ... -0.0] and that is rounded to "-0.000" for printing.
Can anybody explain why and how i can solve this issue?
OP's cosine() lacks sufficient range reduction, which for degrees can be exact.
x %= 360; was a good first step, yet perform a better range reduction to a 90° width like [-45°...45°], [45°...135°], etc.
Also recommend: Use a Taylor series with sufficient terms (e.g. 10) and a good machine PI1. Form the terms more carefully than pow(rad, 2 * n) / fact(2 * n), which inject excessive error.
Example1, example2.
Other improvements possible, yet something to get OP started.
1 #define PI 3.1415926535897932384626433832795

asin() function in C returns NaN when more iterations used

I have a homework in C. We have to write our own asin() function with Taylor method, and we can't use math.h
It works fine, but once I put higher count of iterations(int i), it returns NaN(Not a Number), and when I use low count of i, the number is not exact. Can anyone help me with this?
double my_asin(double x)
{
int i = 0;
double vypocet = x;
double y = vypocet;
for(i=1;i<=10000;i++)
{
vypocet*=((x*x)*(2*i-1)*(2*i-1))/((2*i)*(2*i+1));
y+=vypocet;
}
printf("my_asin = %.10e\n", y);
return y;
}
EDIT: Thank you all! finished it :)
Two things are required for your answer :
Regarding maths : The series expansion you are coding is a sin inverse (arcsin) and expecting an output in radian.
sin^(-1)x=x+1/6x^3+3/(40)x^5+5/(112)x^7+(35)/(1152)x^9+... . As you can see this is an expansion which is monotonically increasing and expecting value (input) between [-1,1] only. When you plug in large values e.g. 10 you are bound to get results you don't expect.So , plug in correct values. I guess, put correct values [-1,1] when calling the function my_asin() and your code would work fine FOR THE number of ITERATIONS YOU HAVE NOW.
e.g 1.5146343691e+000 looks fine for 90 degrees or pi/2 or my_asin(1).
2 .Regarding Floating Point (double i.e. single prrecision floating point ):They cant represent all the numbers on the real line, their range is a subset of R.And when there is a number that can't be represented correctly by their 32 bits encoding (IEEE 754) you will get error in result.
Number as simple as 0.1 cant be represented exactly using floating point.
Check these pages for FP Errors and FP Exceptions :
http://www.gnu.org/software/libc/manual/html_node/Infinity-and-NaN.html
http://www.gnu.org/software/libc/manual/html_node/FP-Exceptions.html#FP-Exceptions

Resources