0

I have been trying to implement a custom sin function that is fast, but more importantly, accurate (I cannot use math.h sin in my project). I'm not an expert when it comes to this kind of math, so work with me XD. After a little searching on the web i found the following code, the function is returning inaccurate results in certain cases.

float SinF(float X)
{
    float Sine;

    if (X < -3.14159265F) X += 6.28318531F;
    else if (X >  3.14159265F) X -= 6.28318531F;

    if (X < 0)
    {
        Sine = 1.27323954F * X + .405284735F * X * X;
        if (Sine < 0) Sine = .225F * (Sine *-Sine - Sine) + Sine;
        else Sine = .225F * (Sine * Sine - Sine) + Sine;
    }
    else
    {
        Sine = 1.27323954F * X - 0.405284735F * X * X;
        if (Sine < 0) Sine = .225F * (Sine *-Sine - Sine) + Sine;
        else Sine = .225F * (Sine * Sine - Sine) + Sine;
    }

    return Sine;
}

Examples:

Bad result example 1:
Value Passed: 1.57079637
Returned Value: 0.999999881
Correct Value: 1.00000000

Bad result example 2:
Value Passed: 1.76704633
Returned Value: 0.980933487
Correct Value: 0.980804682

Bad result example 3:
Value Passed: 1.96329641
Returned Value: 0.924392164
Correct Value: 0.923955679

Any help would be appreciated.

Peter Sutton
  • 1,145
  • 7
  • 20
b.sullender
  • 163
  • 1
  • 16

1 Answers1

2

There's a bunch of potential implementations of sin and friends in this SO question, but typically it boils down to a few usual methods:

  • Built-in processor code (fsin)
  • Taylor series
  • CORDIC
  • Lookup tables with optional linear (or better) interpolation (mainly for speed, less accurate)

There (lots) of other methods but these are the more common ones I've seen.

Also be aware of the inherent precision limits of floating point (as user657267 linked to). For example, 1.57079637 is not exactly pi/2 so its sin() may not be exactly 1. In fact, all your "correct" values listed are not perfectly accurate. You have to decide just how accurate is good enough for your application.

Community
  • 1
  • 1
uesp
  • 6,194
  • 20
  • 15
  • In 32-bit compile mode I'm using the processor code fsin in the _asm keyword, but the _asm keyword is not available in 64-bit mode, so i also need a function to achieve this. The correct values i posted are from the math.h sin function, so that's the accuracy i need. I have tried Taylor series sin, as that seems to be the most common on the web. Il give CORDIC a try. – b.sullender Nov 02 '14 at 00:12
  • `float` only has about 7 significant digits so you are just seeing the limit of how accurate the base type can be. If you need more accuracy consider using `double` instead. – uesp Nov 02 '14 at 00:18
  • Also note that your original code sort of looks like it might be a Taylor series but assumably modified in an attempt at optimization. – uesp Nov 02 '14 at 00:24
  • double might have more precision, but not using double, should not be the problem as both fsin and math.h sin use floats in my case and return the desired values. Correct me if I'm wrong. – b.sullender Nov 02 '14 at 00:30
  • AFAIK, the usual `sin()` from math.h uses `double` (or `long double`) and rounds down from that for the `float` version. Either way, its really up to your specific case as to how much precision you need. If 7 sig-figs is fine then the `float` version will probably do fine. – uesp Nov 02 '14 at 00:34
  • I changed the variables to double, but still getting the wrong values. Perhaps i need to explicitly round down to float instead of just using static_cast (I would have assumed it anyways). any suggestions? – b.sullender Nov 02 '14 at 01:32
  • If you extend to `double` you probably will need to also extend the number of terms used in your approximation function to get more sig-figs. – uesp Nov 02 '14 at 13:15