I have the following function to convert from radians to degrees:
float DegreesToRadians(float degrees) {
return degrees * (PI / 180);
}
When I now do:
sinf(DegreesToRadians(90));
This returns 1, as expected.
But when I do:
sinf(DegreesToRadians(180));
It returns -8.74228e-08. Does anybody know why this is happening? (This happens with cosf too, but in reverse: 180 -> -1; 90 -> -8.74228e-08)
Well, your PI is an approximation of the real mathematical constant. Then your degree-to-radian conversion is an approximation of the real conversion because floating point math is an approximation. And then, the standard sinf function approximates the sin function in math. Thus, you should expect your results to be approximate. -8.74228e-08 is a number close to 0. It's an approximation.
How do I convert degrees to radians?
OP's degrees to radians conversion is reasonable.
return degrees * (PI / 180);
when I do: sinf(DegreesToRadians(180)); It returns -8.74228e-08.
This does not meet OP's expectations as degrees * (PI / 180) was not done exactly given PI is not π #Andy Turner. PI is machine pi, a nearby representable value of π. π is an irrational number. All finite floating point values are rational. There is no way to do non_zero_degrees * (π / 180) exactly. The error in the approximations are amplified in the sinf() call.
sinf(DegreesToRadians(180)) does not result in sine(π), but sine(PI). PI is close to π, but not the same. double graphical example.
Instead, reduce the range of of the angle in degrees first to maintain accuracy with trigonometric identities.
Example
Related
For example I have a value 0.70, and it is a sin() for 45 degrees. I need a function, that will calculate me an angle from a sin. What function from C++ can help me?
You can use std::asin. This returns a value between [-PI/2,+PI/2], to get degrees you multiply by 180 and divide by PI.
result = std::asin(value) * 180 / PI
PI = 3.1415926535
What is the best way to constrain any value from -pi to pi ?
I currently have:
if (fAngle > XM_PI) {
fAngle = fAngle - XM_2PI;
}
else if (fAngle < -XM_PI) {
fAngle = fAngle - -XM_2PI;
}
However, I fear those if's should instead be while's
For reference, under the Exploit Symmetrical Functions section:
https://developer.arm.com/solutions/graphics-and-gaming/developer-guides/learn-the-basics/understanding-numerical-precision/mitigating-loss-of-precision
Extra bit of precision!
Adding or subtracting XM_2PI cannot restore any accuracy that has been lost. In fact, it adds noise, generally losing more accuracy, because XM_2PI is necessarily only an approximation of 2π. It has some error itself, so adding or subtracting it adds or subtracts the error in the approximation.
What it can do is keep you from losing more accuracy by ensuring that future results remain low in magnitude, thus remaining in a region where the floating-point format has more precision than if the number grew beyond 4, 8, 16, or other points where the exponent changes and the absolute precision becomes worse.
If you already have some value x outside [−π, π] and want its sine or cosine, you should get the best result by using sin(x) or cos(x) directly. Good implementations of sin and cos will reduce the argument using a high-precision value for 2π, so you will get a better result than using sin(x-XM_PI) or cos(x-XM_PI) (unless, by chance, the various errors in these happen to cancel).
So your task with trigonometric functions is not to reduce values you already have but to design your algorithms to keep values from growing. Adding or subtracting 2π is a reasonable way to do this. However, when you do it, add or subtract an extended-precision version of 2π, not just XM_2PI. You can do this by representing 2π as XM_2PI (which should be the value representable in floating-point that is closest to 2π) plus some residue r. r should be the value representable in floating-point that is closest to 2π−XM_2PI. You can calculate that with extended-precision software such as GMP or Maple and can likely find it online. (I do not have it handy or I would paste it here; anybody else is welcome to edit it in.) Then you would update your angle with fAngle = fAngle - XM_2PI - r; or fAngle = fAngle + XM_2PI + r;.
An exception is if you have the angle measured in some unit that you can represent or reduce exactly, such as in degrees (which you can reduce by 360º with no error as long as the number of degrees itself is represented with no error) or in time (such as number of seconds for some function with a period of a day or other rational number of seconds, so you can again reduce with no error). In that case, you can let the angle grow as long as you can represent it exactly, and you would reduce it modulo the period prior to converting it to radians.
The simplest coding way is to use the math library function remainder, as in
fAngle = remainder( fangle, XM_2PI);
STATIC_INLINE_PURE float const __vectorcall constrain(float const fAngle)
{
static constexpr double const
dPI(std::numbers::pi),
d2PI(2.0 * std::numbers::pi),
dResidue(-1.74845553146951715461909770965576171875e-07); // abs difference between d2PI(double precision) and XM_2PI(float precision)
double dAngle(fAngle);
dAngle = std::remainder(dAngle, d2PI);
if (dAngle > dPI) {
dAngle = dAngle - d2PI - dResidue;
}
else if (dAngle < -dPI) {
dAngle = dAngle + d2PI + dResidue;
}
return((float)dAngle);
}
I've written a simple program to calculate the first and second derivative of a function, using function pointers. My program computes the correct answers (more or less), but for some functions, the accuracy is less than I would like.
This is the function I am differentiating:
float f1(float x) {
return (x * x);
}
These are the derivative functions, using the central finite difference method:
// Function for calculating the first derivative.
float first_dx(float (*fx)(float), float x) {
float h = 0.001;
float dfdx;
dfdx = (fx(x + h) - fx(x - h)) / (2 * h);
return dfdx;
}
// Function for calculating the second derivative.
float second_dx(float (*fx)(float), float x) {
float h = 0.001;
float d2fdx2;
d2fdx2 = (fx(x - h) - 2 * fx(x) + fx(x + h)) / (h * h);
return d2fdx2;
}
Main function:
int main() {
pc.baud(9600);
float x = 2.0;
pc.printf("**** Function Pointers ****\r\n");
pc.printf("Value of f(%f): %f\r\n", x, f1(x));
pc.printf("First derivative: %f\r\n", first_dx(f1, x));
pc.printf("Second derivative: %f\r\n\r\n", second_dx(f1, x));
}
This is the output from the program:
**** Function Pointers ****
Value of f(2.000000): 4.000000
First derivative: 3.999948
Second derivative: 1.430511
I'm happy with the accuracy of the first derivative, but I believe the second derivative is too far off (it should be equal to ~2.0).
I have a basic understanding of how floating point numbers are represented and why they are sometimes inaccurate, but how can I make this second derivative result more accurate? Could I be using something better than the central finite difference method, or is there a way I can get better results with the current method?
The accuracy can be increased by choosing a type which has more precision. float is currently defined as an IEEE-754 32-bit number, giving you a precision of ~7.225 decimal places.
What you want is the 64-bit counterpart: double with ~15.955 decimal places accuracy.
That should be sufficient for your calculation, however worth mentioning is boosts implementation which offers a quadruple-precision floating point number (128-bit).
Finally The GNU Multiple Precision Arithmetic Library offers types with an arbitrary number of decimal places for precision.
Go analytical. ;-) probably not an option given "with the current
method".
Use double instead of float.
Vary the epsilon (h), and combine the results in some way. For example you could try 0.00001, 0.000001, 0.0000001 and average them. In fact, you'd want the result with the smallest h that doesn't overflow/underflow. But it's not clear how to detect overflow and underflow.
I was having an issue with some floating point math and I've found that if I do my math on one line, I get -0 passed to tan(), and if I do it across two lines, I get 0 passed to tan(). Have a look:
float theta = PI / 2.f;
float p = (PI / 2.f) - theta;
float result = tan(p);
The above, p = -0, result = -4.37...
float theta = PI / 2.f;
float p = PI / 2.f;
p -= theta;
float result = tan(p);
The above, p = 0, result = 0.
Can anyone explain the difference? I assume the -0 is causing that result from tan(), although I can't find anything on google that explains why. Why does the exact same calculation spread across different lines result in a different answer?
Thanks
It is probably because of the type of PI.
If you use double it will change to float and then the outcome
will be as you just represent.
But if PI is float both of this test scenarios are equal.
What #Naor says is probably correct. but I'd like to add something.
You probably not getting -4.37xx but -4.37xxxe-xx which is a pretty small negative number.
Since you can always get errors in floating point math. I'd say there is no need to change your code. Both snips are correct.
So this is what, in my opinion, is happening:
In both examples PI is a define, probably defined like this:
#define 3.14 //and some more digits
In C++, number like this is treated as double.
After preprocessing, this expression:
PI / 2.0f
will be treated as double-typed prvalue. This means that this line hides one more operation:
float theta = PI / 2.f;
which is a double-to-float conversion, which definitely looses some precision in this case.
In first example this also happens here:
float p = (PI / 2.f) - theta;
but only after evaluating whole expression. Note that during this evaluation (PI / 2.f) will be still double, but theta will be a float-to-double converted value, which explains the slight difference in result from 0.0.
In your last example you first convert (PI / 2.f) to float:
float p = PI / 2.f;
to subtract float-typed theta from it in next line. Which must result to 0.0, which probably compiler optimized out anyway ; ).
Hey so i'm doing some graphics math and inserting what i believe to be a float (i'm pretty sure the ways i manipulate the float beforehand is messing something up somehow...) into the function and getting strange, negative results back.
for instance when doing the following opperations when Angle initially equals 350.0, test ends up being
-.99. Why?
Angle= (float)(Angle-(int)Angle)+(float)((int)Angle%90);
// calculates x and y based on angle and Hypotenuse
float test= sin(Angle);
float test2= 1/(Speed*Time);
float test3= test/test2;
buffX= sin(Angle)/ (1.f/(Speed*Time));
buffY= sin(Angle-90)/ (1.f/(Speed*Time));
trying to keep Angle a float by putting (float) before everything didn't work... please help! Thanks!
That's because the C/C++ runtime function sin() expects the argument in radians, not degrees.
Convert to radians with:
float test= sin(Angle / 180 * M_PI);
etc.
sin takes its arguments in radians, not degrees. You need to take you number and multiply it pi/180