Update
This commit is contained in:
parent
68a9873928
commit
b761dff5c7
7 changed files with 49 additions and 13 deletions
49
sin.md
49
sin.md
|
@ -62,18 +62,49 @@ Some values of the sine function are:
|
|||
|
||||
## Programming
|
||||
|
||||
In programming languages sine is generally available in some math library, for example in [C](c.md) the function `sin` is in `math.h`. Spare yourself bugs, **always check if your sin function expects [radians](radian.md) or degrees!**
|
||||
In programming languages the sine function is generally available in some math library, for example in [C](c.md) the function `sin` is in `math.h`. Spare yourself bugs, **always check if your sin function expects [radians](radian.md) or degrees!**
|
||||
|
||||
There exists an **ugly engineering [approximation](approximation.md)** of sine that can be useful sometimes, it says that
|
||||
**Want to make your own sine function for whatever reason (performance, curiosity, ...)?** Then firstly consider what you expect from it. If you want a small, fast and perhaps integer only `sin` function (the one we'd prefer in [LRS](lrs.md)) that doesn't need extreme accuracy, consider using a **[look up table](lut.md)**. You simply precompute the values of the sine function into a static table in memory and the function just retrieves them when called -- this is super fast. Note that you can save a lot of space by **only storing sine values between 0 and 1/2 pi**, the remaining parts of the function are just different transformations of this part. You can further save space and/or make the function work with [floats](float.md) by further [interpolating](interpolation.md) (even just linearly) between the stored values, for example if `sin(3.45)` is called and you only have values stored for `sin(3.4)` and `sin(3.5)`, you simply average them.
|
||||
|
||||
Lot of times, e.g. in many calculators where speed isn't really critical, sine is computed using [Taylor series](taylor_series.md) -- a sum of infinitely many terms of which if we take the first *N*, we get an [approximation](approximation.md) of the function (the more terms we add, the more precise we get). For sine the series is
|
||||
|
||||
*sin(x) = x - x^3 / 3! + x^5 / 5! - x^7 / 7! + ...*
|
||||
|
||||
Adding just the first 3 terms (*x - x^3 / 6 + x^5 / 120*) already gives a very accurate approximation in range <-pi/2,pi/2> (error < 0.5 %). Here is a [C](c.md) function that uses this to compute an 8bit sine (the magic numbers are made so as to incorporate pi while using power of two divisors, also note the use of many operations that will make the function relatively slow):
|
||||
|
||||
```
|
||||
// x = 255 means full angle, returns 0 to 255
|
||||
unsigned char sin8(unsigned char x)
|
||||
{
|
||||
int a = x;
|
||||
char flip = 0;
|
||||
|
||||
if (a > 127)
|
||||
{
|
||||
a -= 128;
|
||||
flip = 1;
|
||||
}
|
||||
|
||||
if (a > 63)
|
||||
a = 128 - a;
|
||||
|
||||
int result = (411999 * a) - (a * a * a * 41);
|
||||
|
||||
a /= 4;
|
||||
|
||||
a = a * a * a * a * a;
|
||||
|
||||
result = (a + result) / 131072;
|
||||
return flip ? (127 - result) : (127 + result);
|
||||
}
|
||||
```
|
||||
|
||||
If you just need a super fast and very rough sine-like value, there exists an **ugly engineering approximation** of sine that can be useful sometimes, it says that
|
||||
|
||||
*sin(x) = x, for small x*
|
||||
|
||||
Indeed, sine looks similar to a mere line near 0, but you can see it quickly diverges.
|
||||
|
||||
When implementing your own `sin` function, consider what you expect from it.
|
||||
|
||||
If you want a small, fast and perhaps integer only `sin` function (the one we'd prefer in [LRS](lrs.md)) that doesn't need extreme accuracy, consider using a **[look up table](lut.md)**. You simply precompute the values of the sine function into a static table in memory and the function just retrieves them when called -- this is super fast. Note that you can save a lot of space by **only storing sine values between 0 and 1/2 pi**, the remaining parts of the function are just different transformations of this part. You can further save space and/or make the function work with [floats](float.md) by further [interpolating](interpolation.md) (even just linearly) between the stored values, for example if `sin(3.45)` is called and you only have values stored for `sin(3.4)` and `sin(3.5)`, you simply average them.
|
||||
|
||||
Very rough and fast approximations e.g. for primitive music synthesis can be done with the traditional very basic [square](square_function.md) or [triangle](triangle_function.md) functions. The following is a simple 8bit linear approximation that's more accurate than square or triangle (approximates sine with a linear function in each octant):
|
||||
|
||||
```
|
||||
|
@ -111,7 +142,7 @@ int sinA(int x)
|
|||
}
|
||||
```
|
||||
|
||||
If you don't need extreme speed there exist very nice sine [approximations](approximation.md), e.g. the extremely accurate **Bhaskara I's approximation** (angle in radians): *sin(x) ~= (16 * x * (pi - x)) / (5 * pi^2 - 4 * x * (pi - x))*. (This formula is actually more elegant for cosine, so it may be even better to consider using that.) Here is a [C](c.md) [fixed point](fixed_point.md) implementation:
|
||||
Furthermore there exist other nice approximations, such as the extremely accurate **Bhaskara I's approximation** (angle in radians): *sin(x) ~= (16 * x * (pi - x)) / (5 * pi^2 - 4 * x * (pi - x))*. (This formula is actually more elegant for cosine, so it may be even better to consider using that.) Here is a [C](c.md) [fixed point](fixed_point.md) implementation:
|
||||
|
||||
```
|
||||
#define UNIT 1024
|
||||
|
@ -142,6 +173,4 @@ int sinInt(int x)
|
|||
|
||||
return sign * (16 * x) / ((5 * PI * PI - 4 * x) / UNIT);
|
||||
}
|
||||
```
|
||||
|
||||
Another approach is to use [Taylor series](taylor_series.md) to approximate sine with a [polynomial](polynomial.md) to whatever precision we need (this is used e.g. in calculators etc.).
|
||||
```
|
Loading…
Add table
Add a link
Reference in a new issue