[Index] [Previous] [Next]

2.13 SIN - Optional Function #3

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 :

 

Scaling

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
0C98 014983 LXI B,8349 BCDE=2p
0C9B 11DB0F LXI D,0FDB
0C9E CD120A CALL FLoadFromBCDE rhs = 2p
0CA1 C1 POP B lhs = x
0CA2 D1 POP D
0CA3 CD3109 CALL FDiv+2 u=x/2p
Lose the integer part of u.
0CA6 CD020A CALL FPush
0CA9 CDA20A CALL Int rhs = INT(u)
0CAC C1 POP B lhs = u
0CAD D1 POP D
0CAE CD0C08 CALL FSub+2 u=u-INT(u)

 

Quadrantization

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
0CB1 01007F LXI B,7F00 BCDE=0.25
0CB4 51 MOV D,C
0CB5 59 MOV E,C
0CB6 CD0C08 CALL FSub+2
If x is +ve then skip ahead having set the carry flag to indicate we do not need to negate
0CB9 EF RST FTestSign
0CBA 37 STC Set carry (ie no later negate)
0CBB F2C30C JP NegateIfPositive

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

0CBE CD0108 CALL FAddOneHalf
0CC1 EF RST 5
0CC2 B7 ORA A Resets carry (ie later negate)
Preserve carry flag and negate x if it's +ve.
0CC3 F5 NegateIfPositive PUSH PSW
0CC4 F4FA09 CP FNegate
Adding 0.25
0CC7 01007F LXI B,7F00 BCDE=0.25
0CCA 51 MOV D,C
0CCB 59 MOV E,C
0CCC CD1208 CALL FAdd+2
Final negate (depends on above).
0CCF F1 POP PSW
0CD0 D4FA09 CNC FNegate

 

Progression Calculation

<fixme>

Push x.
0CD3 CD020A CALL FPush
Push x^2
0CD6 CD1D0A CALL FAccToBCDE
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.
0CDF 21030D LXI H,TAYLOR_SERIES
0CE2 CD0F0A CALL FLoadFromMem
Restore x^2
0CE5 C1 POP B
0CE6 D1 POP D
0CE7 3E04 MVI A,04
0CE9 F5 TaylorLoop PUSH PSW Push #terms remaining
Push x^2
0CEA D5 PUSH D Push BCDE
0CEB C5 PUSH B
q = (q * x^2) + next term
0CEC E5 PUSH H
0CED CDE508 CALL FMul+2
0CF0 E1 POP H
0CF1 CD200A CALL FLoadBCDE
0CF4 E5 PUSH H
0CF5 CD1208 CALL FAdd+2
0CF8 E1 POP H
Restore x^2 to BCDE.
0CF9 C1 POP B
0CFA D1 POP D
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.
0D00 C3E308 JMP FMul

 

The modified Taylor series used by SIN.

0D03 BAD71E86 TAYLOR_SERIES DD 39.710670  
0D07 64269987   DD -76.574982  
0D0B 58342387   DD 81.602234  
0D0F E05DA586   DD -41.341675  
0D13 DA0F4983   DD 6.283185  

 


[Index] [Previous] [Next] DA0F4983   DD 6.283185  

 


[Index] [Previous] [Next]