2 ;; exp, log, sin, asin and friends for bigfloats in Maxima
4 ;; An implementation of the binary splitting algorithms
6 ;; described in http://www.ginac.de/CLN/binsplit.pdf .
9 ;; Copyright (C) 2014 by Volker van Nek
11 ;; Released under the terms of the GNU General Public License
15 ;; Functions at Maxima level
17 ;; bs_asin(x), bs_acos(x), where 0 <= x <= 1
18 ;; bs_atan(x), bs_acot(x) 0 <= x
19 ;; bs_sin(x), bs_cos(x) 0 <= x <= pi/2
20 ;; bs_tan(x), bs_cot(x) 0 <= x < pi/2
21 ;; bs_exp(x) 0 <= x < 1
23 ;; bs_expm1(x) 0 <= x < log(2)
24 ;; bs_log1p(eps) eps > -1
25 ;; x and eps are real valued bigfloats
28 ;;----------------------------------------------------------------------------;;
29 ;; Compute phi = asin(x) or acos(x), where 0 <= x <= 1
31 ;; or phi = atan(x) or acot(x), where 0 <= x.
33 (defun $bs_asin
(x) (bs-asincos x
'asin
)) ;; top level test functions
34 (defun $bs_acos
(x) (bs-asincos x
'acos
))
35 (defun $bs_atan
(x) (bs-asincos x
'atan
))
36 (defun $bs_acot
(x) (bs-asincos x
'acot
))
38 (defun bs-asincos (x fn
)
39 (let ((phi ;; phi = carg(z), where ..
40 (let* ((fpprec (+ fpprec
12)) ;; needs testing
41 (x (cdr (bigfloatp x
)))
46 ((or (eq fn
'asin
) (eq fn
'acos
))
47 (setq y
(fproot (bcons (fpdifference one xx
)) 2))
49 ;; asin: .. z = sqrt(1-x^2) + %i * x
51 ;; acos: .. z = x + %i * sqrt(1-x^2)
52 (setq re-z x im-z y
) ))
54 (setq y
(fpquotient one
(fproot (bcons (fpplus one xx
)) 2)))
56 ;; atan: .. z = 1/sqrt(x^2+1) + %i * x/sqrt(x^2+1)
57 (setq re-z y im-z
(fptimes* x y
))
58 ;; acot: .. z = x/sqrt(x^2+1) + %i * 1/sqrt(x^2+1)
59 (setq re-z
(fptimes* x y
) im-z y
) )))
60 (bcons (bs-carg1 re-z im-z fpprec
)) )))
63 ;;----------------------------------------------------------------------------;;
65 ;;----------------------------------------------------------------------------;;
66 ;; Compute phi = carg(z) in quadrant I, where |z| = 1.
68 ;; im(z) < 1/2: Algorithm bs-carg11 moves z along the unit circle down to the real axis
70 ;; while counting the angular movement (Haible & Papanikolaou, 2.2.8).
72 ;; im(z) < 1/sqrt(2): phi/2 = carg(sqrt(z)).
74 ;; im(z) = 1/sqrt(2): phi = pi/4.
76 ;; im(z) > 1/sqrt(2): pi/2 - phi = carg(imagpart(z) + %i*realpart(z)).
78 (defun bs-carg1 (re-z im-z prec
) ;; assumes |z| = 1, quadrant 1
80 ((zerop (car (fpdifference re-z im-z
)))
81 (let ((%pi
(fppi))) (list (car %pi
) (- (cadr %pi
) 2))) ) ;; pi/4
82 ((fpgreaterp im-z re-z
) ;; im-z > 1/sqrt(2) (i.e. phi > pi/4)
84 (%pi
/2 (list (car %pi
) (1- (cadr %pi
)))) )
85 (fpdifference %pi
/2 (bs-carg1 im-z re-z prec
)) ))
88 ;; bs-carg11 needs im-z < 1/2 (i.e. phi < pi/6)
89 ;; when im-z >= 1/2 bisect phi by taking the complex sqrt
90 (when (not (fplessp im-z
(cdr bfhalf
)))
91 (multiple-value-setq (re-z im-z
) (complex-sqrt (bcons re-z
) (bcons im-z
)))
93 (setq phi
(bs-carg11 (list re-z im-z
) prec
))
94 (list (car phi
) (+ (cadr phi
) e
)) ))))
96 (defun bs-carg11 (z prec
) ;; assumes |z| = 1, 0 < Re(z) <= 1, 0 <= Im(z) < 1/2
101 (im-z (cadr z
) (cadr z
)) )
102 ((not (fpgreaterp (fpplus one im-z
) one
)) phi
) ;; stop when im-z = 0 within current prec
103 (setq k
(- (cadr im-z
)) ;; im-z < 2^-k
104 u
(ldb (byte k
(- prec k
)) (car im-z
)) ;; first k significant mantissa bit
105 e
(complex-exp-%i
*u
/2^
2k
(- u
) k prec
)
106 z
(complex-fptimes* z e
)
108 alpha
(list (car alpha
) (- (cadr alpha
) (* 2 k
)))
109 phi
(fpplus phi alpha
) ))))
111 (defun complex-exp-%i
*u
/2^
2k
(u k prec
) ;; ;; compute exp(%i*u/2^2k) where u < 2^k, integer u,k
113 (list (fpone) (list 0 0))
115 (kk k
) ) ;; see (**) below why k is param for bs-exp-u/2^z-series-size
116 (complex-exp-%i
*u
/2^z u z kk prec
) )))
118 (defun split-bs-exp-u/2^z
(i j u z
) ;; u is integer or complex with integer parts
122 (setq pp
1 qq
1 zz
0 tt
1)
123 (setq pp u qq i zz z tt pp
) )
124 (let ((m (ash (+ i j
) -
1)))
125 (multiple-value-bind (tl zl ql pl
) (split-bs-exp-u/2^z i m u z
)
126 (multiple-value-bind (tr zr qr pr
) (split-bs-exp-u/2^z m j u z
)
133 (mapcar #'(lambda (n) (ash n zr
))
134 (list (realpart tt
) (imagpart tt
)) ))
136 tt
(+ tt
(* pl tr
)) )))))
137 (values tt zz qq pp
) ))
139 (defun complex-fptimes* (a b
)
140 (let ((x1 (car a
)) (y1 (cadr a
))
141 (x2 (car b
)) (y2 (cadr b
)) )
143 (fpdifference (fptimes* x1 x2
) (fptimes* y1 y2
))
144 (fpplus (fptimes* x1 y2
) (fptimes* x2 y1
)) )))
146 (defun complex-fpsquare (a)
147 (let* ((x (car a
)) (y (cadr a
))
148 (xy (fptimes* x y
)) )
150 (fpdifference (fptimes* x x
) (fptimes* y y
))
151 (list (car xy
) (1+ (cadr xy
))) )))
153 ;;----------------------------------------------------------------------------;;
156 ;;----------------------------------------------------------------------------;;
157 ;; Compute sin(x), cos(x), tan(x), cot(x), where 0 <= x <= %pi/2
159 (defun $bs_sin
(x) (bs-sincos x
'sin
)) ;; top level test functions
160 (defun $bs_cos
(x) (bs-sincos x
'cos
))
161 (defun $bs_tan
(x) (bs-sincos x
'tan
))
162 (defun $bs_cot
(x) (bs-sincos x
'cot
))
164 (defun bs-sincos (x fn
) ;; 0 <= x <= pi/2
165 (let ((xx (cdr x
)) pi
/1 pi
/2 dx
)
167 ((or (= 0 (car xx
)) (< (cadr xx
) (- fpprec
))) ;; x is zero within current prec
168 (cond ((eql fn
'sin
) xx
)
169 ((eql fn
'cos
) (intofp 1))
171 ((eql fn
'cot
) (merror (intl:gettext
"attempted quotient by zero."))) ))
172 ((and (setq pi
/1 (fppi)
173 pi
/2 (list (car pi
/1) (1- (cadr pi
/1)))
174 dx
(fpdifference pi
/2 xx
) )
175 (or (= 0 (car dx
)) (< (cadr dx
) (- fpprec
))) ) ;; x is pi/2 within current prec
176 (cond ((eql fn
'sin
) (intofp 1))
178 ((eql fn
'tan
) (merror (intl:gettext
"attempted quotient by zero.")))
179 ((eql fn
'cot
) dx
) ))
182 (let* ((fpprec (+ fpprec
12)) ;; needs testing
183 (x (cdr (bigfloatp x
))) )
185 (multiple-value-bind (re im
) (bs-exp-%i
*x x fpprec
)
186 (cond ((eq fn
'sin
) im
)
188 ((eq fn
'tan
) (fpquotient im re
))
189 ((eq fn
'cot
) (fpquotient re im
)) ))))))
190 (bigfloatp res
) )))))
192 (defun bs-exp-%i
*x
(x prec
) ;; assume 0 <= x <= pi/2
193 (let ((mant (car x
)) (e (cadr x
))
194 (acc (list (intofp 1) (list 0 0))) ;; complex-(1,0)
196 (when (fpgreaterp x
(floattofp (/ pi
4))) ;; is x > pi/4 ?
197 (setq e
(1- e
) ;; x <-- x/2
199 ;; now x <= pi/4 (the algorithm needs x < 1)
202 (pos1 1 (ash pos1
1))
205 (setq pos2
(- prec
(+ pos1 e
)))
207 (setq mant
(ash mant
(- pos2
))
209 (setq u
(ldb (byte nr pos2
) mant
)
210 tmp
(complex-exp-%i
*u
/2^
2^k u k prec
)
211 acc
(complex-fptimes* acc tmp
)
213 (when x
/2?
(setq acc
(complex-fpsquare acc
))) ;; exp(%i*x) = exp(%i*x/2)^2
214 (apply #'values acc
) ))
216 (defun complex-exp-%i
*u
/2^
2^k
(u k prec
) ;; compute exp(%i*u/2^2^k) where u < 2^2^(k-1) resp. 2^-1 for k=0, integer u,k
218 (list (fpone) (list 0 0))
220 (kk (if (= k
0) 1/2 (ash 1 (1- k
)))) ) ;; param for bs-exp-u/2^z-series-size, see (*)
221 (complex-exp-%i
*u
/2^z u z kk prec
) )))
223 (defun complex-exp-%i
*u
/2^z
(u z kk prec
)
224 (let ((nr (bs-exp-u/2^z-series-size kk prec
))
228 (setq u
(ash u
(- i
)) z
(- z i
)) )
230 (multiple-value-bind (tt zz qq
) (split-bs-exp-u/2^z
0 (1+ nr
) (complex 0 u
) z
)
232 x
(fpquotient (intofp (realpart tt
)) qq
)
233 y
(fpquotient (intofp (imagpart tt
)) qq
)
234 x
(list (car x
) (- (cadr x
) zz
))
235 y
(list (car y
) (- (cadr y
) zz
)) )
238 ;;----------------------------------------------------------------------------;;
241 ;;----------------------------------------------------------------------------;;
242 ;; exp(x)-1 for 0 <= x < log(2)
244 ;; x = mantissa*2^e = 2^(e-1) + ... where e <= 0
246 ;; exp(x) = 1 + x + x^2/2! + ... = 1 + 2^(e-1) + ...
248 ;; exp(x)-1 looses |e-1| bits precision
250 (defun $bs_expm1
(x) (bs-expm1 x
)) ;; top level test function
252 (defun bs-expm1 (x) ;; 0 <= x < log(2)
253 (setq x
(bigfloatp x
))
254 (let* ((extra (1+ (- (caddr x
))))
256 (let ((fpprec (+ fpprec extra
)))
257 (bcons (fpdifference (cdr (bs-exp x
)) (fpone))) )))
260 ;;----------------------------------------------------------------------------;;
262 ;; exp(x), real 0 <= x < 1
264 (defun $bs_exp
(x) (bs-exp x
)) ;; top level test function
266 (defun bs-exp (x) ;; assume 0 <= x < 1
268 (let* ((fpprec (+ fpprec
12)) ;; needs testing
269 (x (cdr (bigfloatp x
)))
270 (mant (car x
)) (e (cadr x
))
272 (nr 1) ;; nr of extracted bits
275 (pos1 1 (ash pos1
1))
277 ((= 0 pos2
) (bcons res
))
278 (setq pos2
(- fpprec
(+ pos1 e
)))
280 (setq mant
(ash mant
(- pos2
))
282 (setq u
(ldb (byte nr pos2
) mant
)
283 res
(fptimes* res
(bs-exp-u/2^
2^k u k fpprec
))
287 (defun bs-exp-u/2^
2^k
(u k prec
) ;; compute exp(u/2^2^k) where u < 2^2^(k-1), integer u,k (k >= 0)
291 (kk (if (= k
0) 1/2 (ash 1 (1- k
)))) ) ;; param for bs-exp-u/2^z-series-size, see (*)
292 (bs-exp-u/2^z u z kk prec
) )))
294 ;; (*) End the Taylor-series when (u/2^2^k)^i/i! < 1/2^fpprec.
296 ;; u might not fit into a float. Use bound u < 2^2^(k-1), i.e. u/2^2^k < 1/2^2^(k-1).
298 ;; So end when i!*(2^2^(k-1))^i > 2^fpprec or
300 ;; log(i!) + i * 2^(k-1)*log(2) = sum(log(n) + 2^(k-1)*log(2), n,1,i) > fpprec * log(2).
302 (defun bs-exp-u/2^z-series-size
(kk prec
)
303 (setq kk
(* kk
(log 2)))
306 (lim (* prec
(log 2))) )
308 (incf acc
(+ (log n
) kk
)) ))
310 (defun bs-exp-u/2^z
(u z kk prec
) ;; compute exp(u/2^z), integer u,z
314 (setq u
(ash u
(- i
))
317 (setq nr
(bs-exp-u/2^z-series-size kk prec
))
318 (multiple-value-bind (tt zz qq
) (split-bs-exp-u/2^z
0 (1+ nr
) u z
)
319 (setq tt
/qq
(fpquotient (intofp tt
) (intofp qq
)))
320 (list (car tt
/qq
) (- (cadr tt
/qq
) zz
)) )))
321 ;;----------------------------------------------------------------------------;;
324 ;;----------------------------------------------------------------------------;;
326 ;; Compute log(1 + eps), real eps > -1
328 (defun $bs_log1p
(eps) (bs-log1p eps
)) ;; top level test function
330 (defun bs-log1p (eps) ;; eps = x - 1
331 (setq eps
(bigfloatp eps
))
333 (let* ((extra (1+ (- (caddr eps
)))) ;; give |e-1| extra bits
334 (fpprec (+ fpprec extra
))
335 ;; x = 1 + eps = m*2^e = 2^(e-1) + ...
336 (x (bcons (fpplus (cdr (bigfloatp eps
)) (fpone)))) )
340 ;;----------------------------------------------------------------------------;;
342 ;; Compute log(x), real x > 0
344 ;; Use log(x) = log(x/2^k) + k*log(2), where x/2^k < 1.
346 (defun $bs_log
(x) (bs-log x
)) ;; top level test function
348 (defun bs-log (x) ;; assume x > 0
350 (let* ((fpprec (+ fpprec
12)) ;; needs more testing
351 (x (cdr (bigfloatp x
)))
352 (m (car x
)) (k (cadr x
)) ;; mantissa, exponent
355 (setq x
(list m
0)) ;; now x = 0 or 1/2 <= x < 1
356 ;; choose k so that |x - 1| is as small as possible:
357 (when (fplessp x
(floattofp (coerce 2/3 'flonum
)))
360 ;; now |x - 1| <= 1/3 < 1/2
361 (setq lgx
(bs-log1 x fpprec
))
365 (fpplus lgx
(fptimes* (intofp k
) (comp-log2))) )))))
368 (defun bs-log1 (x prec
) ;; assume |x - 1| < 1/2
371 (do ((y (list 0 0)) (k 0) m-len
(pos 1))
373 (setq dx
(fpdifference x one
)
376 (unless (fpposp dx
) (return y
))
377 (setq k
(- (cadr dx
))) ;; |x - 1| < 2^-k
378 (setq m-len
(integer-length (car dx
)) ;; m-len isn't always prec
380 u
(ldb (byte k pos
) (car dx
)) ;; first k significant mantissa bits
382 e
(bs-exp-u/2^
2k
(- u
) k prec
)
385 z
(list (car z
) (- (cadr z
) (* 2 k
)))
388 (defun bs-exp-u/2^
2k
(u k prec
) ;; compute exp(u/2^2k) where u < 2^k, integer u,k (k >= 0)
391 (bs-exp-u/2^z u
(* 2 k
) k prec
) )) ;; k is param for bs-exp-u/2^z-series-size, see (**)
393 ;; (**) End the Taylor-series when (u/2^(2*k))^i/i! < 1/2^fpprec.
395 ;; u might not fit into a float. Use bound u < 2^k, i.e. u/2^(2*k) < 1/2^k.
397 ;; So end when i!*(2^k)^i > 2^fpprec or
399 ;; log(i!) + i * k*log(2) = sum(log(n) + k*log(2), n,1,i) > fpprec * log(2).
401 ;;----------------------------------------------------------------------------;;