Returns the sine of the radians argument, which we'll call x. This is the most mathematically-intense function, and I have to thank Ian Griffiths again for doing the hard work behind the analysis presented here.
The MacLaurin Series
Altair BASIC's calculation of the sine function is based upon the MacLaurin series. A MacLaurin series is an expansion of functions whose derivatives are continuous.
Now, how do we use this series for the sine function? It's going to look like this :
Well, we know that the derivative of sin(x) is cos(x), and the derivative of cos(x) is -sin(x). We also know that sin(0)=0, and cos(x)=1. Therefore the series can be reduced to :
Simplifying a bit further, and limiting the number of terms to a reasonable approximation, we get down to :
To make the code in this and the next section smaller, our first step is to scale our radians argument x into a new variable u that is in the range -1 < u< 1. To do this we first divide x by 2p and then lose the integer part of that number.
|Divide x (in FACCUM) by 2p to get u.|
|0C95||CD020A||Sin||CALL FPush||Push x|
|0C9E||CD120A||CALL FLoadFromBCDE||rhs = 2p|
|0CA1||C1||POP B||lhs = x|
|Lose the integer part of u.|
|0CA9||CDA20A||CALL Int||rhs = INT(u)|
|0CAC||C1||POP B||lhs = u|
The second part of the algorithm is an optimisation to reduce u to a 'quadrantized' value, q, so called because it lies within the two quadrants either side of the origin, ie -0.25<q<0.25. Here's a little graph showing sin(x) against u, with the quadrant numbers shown in red.
To get from u to q, we don't need to scale again but we do take advantage of two trignometric identities : sin(-x) = -sin(x), and sin(x)=sin(pi-x).
<fixme: insert Monte's explanation here>
So we have our value q. Here's another graph showing sin(x) against the quadrantised value q.
fixme: the comments inlined below are train-of-thought and should not be used. I haven't quite worked this out.
|Firstly we subtract from 0.25 to get x from -0.75<=x<1.25|
|If x is +ve then skip ahead having set the carry flag to indicate we do not need to negate|
|0CBA||37||STC||Set carry (ie no later negate)|
x is between -0.75 and 0. Here we add 0.5 to get x between -0.25 and 0.25 and signal that negation is required
|0CC2||B7||ORA A||Resets carry (ie later negate)|
|Preserve carry flag and negate x if it's +ve.|
|Final negate (depends on above).|
|0CD9||CDE508||CALL FMul+2||x = x*x|
|0CDC||CD020A||CALL FPush||Push x*x|
|Let q be the first term of the Taylor series.|
|0CE9||F5||TaylorLoop||PUSH PSW||Push #terms remaining|
|0CEA||D5||PUSH D||Push BCDE|
|q = (q * x^2) + next term|
|Restore x^2 to BCDE.|
|0CFB||F1||POP PSW||Pop #terms remaining into A.|
|0CFC||3D||DCR A||Decrement #terms and loop back if not|
|0CFD||C2E90C||JNZ TaylorLoop||done all 4 of them.|
|Finally multiply q by x.|
The modified Taylor series used by SIN.
[Index] [Previous] [Next]