calc: Sine Example
18.5.5.2 The Sine Function
..........................
A somewhat limited sine function could be defined as follows, using the
well-known Taylor series expansion for ‘sin(x)’:
(defmath mysin ((float (anglep x)))
(interactive 1 "mysn")
(setq x (to-radians x)) ; Convert from current angular mode.
(let ((sum x) ; Initial term of Taylor expansion of sin.
newsum
(nfact 1) ; "nfact" equals "n" factorial at all times.
(xnegsqr :"-(x^2)")) ; "xnegsqr" equals -x^2.
(for ((n 3 100 2)) ; Upper limit of 100 is a good precaution.
(working "mysin" sum) ; Display "Working" message, if enabled.
(setq nfact (* nfact (1- n) n)
x (* x xnegsqr)
newsum (+ sum (/ x nfact)))
(if (~= newsum sum) ; If newsum is "nearly equal to" sum,
(break)) ; then we are done.
(setq sum newsum))
sum))
The actual ‘sin’ function in Calc works by first reducing the problem
to a sine or cosine of a nonnegative number less than ‘pi/4’. This
ensures that the Taylor series will converge quickly. Also, the
calculation is carried out with two extra digits of precision to guard
against cumulative round-off in ‘sum’. Finally, complex arguments are
allowed and handled by a separate algorithm.
(defmath mysin ((float (scalarp x)))
(interactive 1 "mysn")
(setq x (to-radians x)) ; Convert from current angular mode.
(with-extra-prec 2 ; Evaluate with extra precision.
(cond ((complexp x)
(mysin-complex x))
((< x 0)
(- (mysin-raw (- x))) ; Always call mysin-raw with x >= 0.
(t (mysin-raw x))))))
(defmath mysin-raw (x)
(cond ((>= x 7)
(mysin-raw (% x (two-pi)))) ; Now x < 7.
((> x (pi-over-2))
(- (mysin-raw (- x (pi))))) ; Now -pi/2 <= x <= pi/2.
((> x (pi-over-4))
(mycos-raw (- x (pi-over-2)))) ; Now -pi/2 <= x <= pi/4.
((< x (- (pi-over-4)))
(- (mycos-raw (+ x (pi-over-2))))) ; Now -pi/4 <= x <= pi/4,
(t (mysin-series x)))) ; so the series will be efficient.
where ‘mysin-complex’ is an appropriate function to handle complex
numbers, ‘mysin-series’ is the routine to compute the sine Taylor series
as before, and ‘mycos-raw’ is a function analogous to ‘mysin-raw’ for
cosines.
The strategy is to ensure that ‘x’ is nonnegative before calling
‘mysin-raw’. This function then recursively reduces its argument to a
suitable range, namely, plus-or-minus ‘pi/4’. Note that each test, and
particularly the first comparison against 7, is designed so that small
roundoff errors cannot produce an infinite loop. (Suppose we compared
with ‘(two-pi)’ instead; if due to roundoff problems the modulo operator
ever returned ‘(two-pi)’ exactly, an infinite recursion could result!)
We use modulo only for arguments that will clearly get reduced, knowing
that the next rule will catch any reductions that this rule misses.
If a program is being written for general use, it is important to
code it carefully as shown in this second example. For quick-and-dirty
programs, when you know that your own use of the sine function will
never encounter a large argument, a simpler program like the first one
shown is fine.