1 ;;;; -*- Mode: lisp -*-
3 ;;;; Copyright (c) 2007 Raymond Toy
5 ;;;; Permission is hereby granted, free of charge, to any person
6 ;;;; obtaining a copy of this software and associated documentation
7 ;;;; files (the "Software"), to deal in the Software without
8 ;;;; restriction, including without limitation the rights to use,
9 ;;;; copy, modify, merge, publish, distribute, sublicense, and/or sell
10 ;;;; copies of the Software, and to permit persons to whom the
11 ;;;; Software is furnished to do so, subject to the following
14 ;;;; The above copyright notice and this permission notice shall be
15 ;;;; included in all copies or substantial portions of the Software.
17 ;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 ;;;; EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 ;;;; OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 ;;;; NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 ;;;; HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 ;;;; WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 ;;;; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 ;;;; OTHER DEALINGS IN THE SOFTWARE.
26 ;;; Basic special functions operating on %quad-double numbers. This
27 ;;; includes sqrt, rounding to the nearest integer, floor, exp, log,
28 ;;; log1p, sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh,
29 ;;; asinh, acosh, atanh, and random.
31 ;;; These special functions only work on the main domains where the
32 ;;; argument is real and the result is real. Behavior is undefined if
33 ;;; this doesn't hold.
38 (declaim (maybe-inline sqrt-qd
))
40 "Square root of the (non-negative) quad-float"
41 (declare (type %quad-double a
)
42 (optimize (speed 3) (space 0)))
43 ;; Perform the following Newton iteration:
45 ;; x' = x + (1 - a * x^2) * x / 2
47 ;; which converges to 1/sqrt(a).
49 ;; However, there appear to be round-off errors when x is either
50 ;; very large or very small. So let x = f*2^(2*k). Then sqrt(x) =
51 ;; 2^k*sqrt(f), and sqrt(f) doesn't have round-off problems.
53 (return-from sqrt-qd a
))
55 (when (float-infinity-p (qd-0 a
))
56 (return-from sqrt-qd a
))
58 (let* ((k (logandc2 (logb-finite (qd-0 a
)) 1))
59 (new-a (scale-float-qd a
(- k
))))
61 (let* ((r (make-qd-d (cl:/ (sqrt (the (double-float (0d0))
64 (h (mul-qd-d new-a half
)))
65 (declare (type %quad-double r
))
66 ;; Since we start with double-float precision, three more
67 ;; iterations should give us full accuracy.
69 (setf r
(add-qd r
(mul-qd r
(sub-d-qd half
(mul-qd h
(sqr-qd r
)))))))
70 (scale-float-qd (mul-qd r new-a
) (ash k -
1)))))
72 (defun logb-finite (x)
73 "Same as logb but X is not infinity and non-zero and not a NaN, so
74 that we can always return an integer"
75 (declare (type cl
:float x
))
76 (multiple-value-bind (signif expon sign
)
78 (declare (ignore signif sign
))
79 ;; decode-float is almost right, except that the exponent
83 (defun hypot-aux-qd (x y
)
84 (declare (type %quad-double x y
))
85 (let ((k (- (logb-finite (max (cl:abs
(qd-0 x
))
86 (cl:abs
(qd-0 y
)))))))
87 (values (add-qd (sqr-qd (scale-float-qd x k
))
88 (sqr-qd (scale-float-qd y k
)))
92 "sqrt(x^2+y^2) computed carefully without unnecessary overflow"
93 (multiple-value-bind (abs^
2 rho
)
95 (scale-float-qd (sqrt-qd abs^
2) rho
)))
98 "Round the quad-float to the nearest integer, which is returned as a
100 (let ((x0 (fround (qd-0 a
)))
104 (cond ((= x0
(qd-0 a
))
105 ;; First double is already an integer
106 (setf x1
(fround (qd-1 a
)))
107 (cond ((= x1
(qd-1 a
))
108 ;; Second is an integer
109 (setf x2
(fround (qd-2 a
)))
110 (cond ((= x2
(qd-2 a
))
111 ;; Third is an integer
112 (setf x3
(fround (qd-3 a
))))
114 (when (and (zerop (abs (cl:- x2
(qd-2 a
))))
118 (when (and (zerop (abs (cl:- x1
(qd-1 a
))))
122 (when (and (zerop (abs (cl:- x0
(qd-0 a
))))
125 (multiple-value-bind (s0 s1 s2 s3
)
126 (renorm-4 x0 x1 x2 x3
)
127 (make-qd-d s0 s1 s2 s3
))))
130 "The floor of A, returned as a quad-float"
131 (let ((x0 (ffloor (qd-0 a
)))
135 (cond ((= x0
(qd-0 a
))
136 (setf x1
(ffloor (qd-1 a
)))
137 (when (= x1
(qd-1 a
))
138 (setf x2
(ffloor (qd-2 a
)))
139 (when (= x2
(qd-2 a
))
140 (setf x3
(ffloor (qd-3 a
)))))
141 (make-qd-d x0 x1 x2 x3
))
143 (%make-qd-d x0 x1 x2 x3
)))))
146 (defun exp-qd/reduce
(a)
147 ;; Strategy: Reduce the size of x by noting that
149 ;; exp(k*r+m) = exp(m) * exp(r)^k
151 ;; Thus, by choosing m to be a multiple of log(2) closest to x, we
152 ;; can make |kr| < log(2)/2 = 0.3466. Now we can set k = 256, so
153 ;; that |r| <= 0.00136.
157 ;; exp(x) = exp(k*r+s*log(2)) = 2^s*(exp(r))^256
159 ;; We can use Taylor series to evaluate exp(r).
162 (z (truncate (qd-0 (nint-qd (div-qd a
+qd-log2
+)))))
163 (r1 (sub-qd a
(mul-qd-d +qd-log2
+ (float z
1d0
))))
165 (r (div-qd-d (sub-qd a
(mul-qd-d +qd-log2
+ (float z
1d0
)))
167 ;; For Taylor series. p = r^2/2, the first term
168 (p (div-qd-d (sqr-qd r
) 2d0
))
169 ;; s = 1+r+p, the sum of the first 3 terms
170 (s (add-qd-d (add-qd r p
) 1d0
))
171 ;; Denominator of term
173 ;; Taylor series until the term is small enough.
175 ;; Note that exp(x) = sinh(x) + sqrt(1+sinh(x)^2). The Taylor
176 ;; series for sinh has half as many terms as for exp, so it should
177 ;; be less work to compute sinh. Then a few additional operations
178 ;; and a square root gives us exp.
181 (setf p
(mul-qd p r
))
182 (setf p
(div-qd-d p m
))
183 (setf s
(add-qd s p
))
184 (unless (> (abs (qd-0 p
)) +qd-eps
+)
188 (setf r
(scale-float-qd r z
))
191 (defun expm1-qd/duplication
(a)
192 (declare (type %quad-double a
))
193 ;; Brent gives expm1(2*x) = expm1(x)*(2+expm1(x))
197 ;; expm1(x) = expm1(x/2)*(2+expm1(x/2))
199 ;; Keep applying this formula until x is small enough. Then use
200 ;; Taylor series to compute expm1(x).
201 (cond ((< (abs (qd-0 a
)) .0001d0
)
202 ;; What is the right threshold?
204 ;; Taylor series for exp(x)-1
205 ;; = x+x^2/2!+x^3/3!+x^4/4!+...
206 ;; = x*(1+x/2!+x^2/3!+x^3/4!+...)
210 (setf term
(div-qd-d (mul-qd term a
) (float (cl:+ k
2) 1d0
)))
211 (setf sum
(add-qd sum term
)))
214 (let ((d (expm1-qd/duplication
(scale-float-qd a -
1))))
215 (mul-qd d
(add-qd-d d
2d0
))))))
218 "exp(a) - 1, done accurately"
219 (declare (type %quad-double a
))
221 (when (float-infinity-p (qd-0 a
))
222 (return-from expm1-qd
223 (if (minusp (float-sign (qd-0 a
)))
226 (expm1-qd/duplication a
))
230 (declare (type %quad-double a
))
231 ;; Should we try to be more accurate than just 709?
232 (when (< (qd-0 a
) (log least-positive-normalized-double-float
))
233 (return-from exp-qd
+qd-zero
+))
235 (when (> (qd-0 a
) (log most-positive-double-float
))
237 (error 'floating-point-overflow
241 (return-from exp-qd
(%make-qd-d
(/ 1d0
0d0
) 0d0
0d0
0d0
)))
244 (return-from exp-qd
+qd-one
+))
246 ;; Default for now is exp-qd/reduce
249 (defun log-qd/halley
(a)
250 (declare (type %quad-double a
))
253 ;; x' = x - 2*(exp(x)-a)/(exp(x)+a)
255 ;; But the above has problems if a is near
256 ;; most-positive-double-float. Rearrange the computation:
258 ;; x' = x - 2*(exp(x)/a-1)/(exp(x)/a+1)
260 ;; I think this works better, but it's also probably a little bit
261 ;; more expensive because each iteration has two divisions.
262 (let ((x (make-qd-d (log (qd-0 a
)))))
264 (let ((exp (div-qd (exp-qd est
)
268 (div-qd (sub-qd-d exp
1d0
)
271 ;; Two iterations should be enough
277 (defun log1p-qd/duplication
(x)
278 (declare (type %quad-double x
)
279 (optimize (speed 3)))
280 ;; Brent gives the following duplication formula for log1p(x) =
283 ;; log1p(x) = 2*log1p(x/(1+sqrt(1+x)))
285 ;; So we apply the duplication formula until x is small enough, and
286 ;; then use the series
288 ;; log(1+x) = 2*sum((x/(2+x))^(2*k+1)/(2*k+1),k,0,inf)
290 ;; Currently "small enough" means x < 0.005. What is the right
292 (cond ((> (abs (qd-0 x
)) .005d0
)
293 ;; log1p(x) = 2*log1p(x/(1+sqrt(1+x)))
294 (mul-qd-d (log1p-qd/duplication
297 (sqrt-qd (add-d-qd 1d0 x
)))))
301 (let* ((term (div-qd x
(add-qd-d x
2d0
)))
304 (loop for k of-type double-float from
3d0 by
2d0
305 while
(> (abs (qd-0 term
)) +qd-eps
+)
307 (setf term
(mul-qd term mult
))
308 (setf sum
(add-qd sum
(div-qd-d term k
))))
309 (mul-qd-d sum
2d0
)))))
312 "log1p(x) = log(1+x), done more accurately than just evaluating
314 (declare (type %quad-double x
))
316 (when (float-infinity-p (qd-0 x
))
318 (log1p-qd/duplication x
))
322 (declare (type %quad-double a
))
326 (plusp (float-sign (qd-0 a
))))
327 (%make-qd-d
(/ -
1d0
(qd-0 a
)) 0d0
0d0
0d0
))
329 ((float-infinity-p (qd-0 a
))
331 ((minusp (float-sign (qd-0 a
)))
332 (error "log of negative"))
334 ;; Default is Halley's method
338 ;; sin(a) and cos(a) using Taylor series
340 ;; Assumes |a| <= pi/2048
341 (defun sincos-taylor (a)
342 (declare (type %quad-double a
))
343 (let ((thresh (cl:* +qd-eps
+ (abs (qd-0 a
)))))
345 (return-from sincos-taylor
348 (let* ((x (neg-qd (sqr-qd a
)))
353 (setf p
(mul-qd p x
))
355 (setf p
(div-qd-d p
(cl:* m
(cl:1- m
))))
356 (setf s
(add-qd s p
))
357 ;;(format t "p = ~A~%" (qd-0 p))
358 (when (<= (abs (qd-0 p
)) thresh
)
360 ;; cos(c) = sqrt(1-sin(c)^2). This seems to work ok, even
361 ;; though I would have expected some round-off errors in
362 ;; computing this. sqrt(1-x^2) is normally better computed as
363 ;; sqrt(1-x)*sqrt(1+x) for small x.
364 (values s
(sqrt-qd (add-qd-d (neg-qd (sqr-qd s
)) 1d0
))))))
367 (declare (type %quad-double a b
))
368 (let ((n (nint-qd (div-qd a b
))))
369 (sub-qd a
(mul-qd n b
))))
371 (defun divrem-qd (a b
)
372 (declare (type %quad-double a b
))
373 (let ((n (nint-qd (div-qd a b
))))
374 (values n
(sub-qd a
(mul-qd n b
)))))
378 (declare (type %quad-double a
))
379 ;; To compute sin(x), choose integers a, b so that
381 ;; x = s + a * (pi/2) + b*(pi/1024)
383 ;; with |x| <= pi/2048. Using a precomputed table of sin(k*pi/1024)
384 ;; and cos(k*pi/1024), we can compute sin(x) from sin(s) and cos(s).
386 ;; sin(x) = sin(s+k*(pi/1024) + j*pi/2)
387 ;; = sin(s+k*(pi/1024))*cos(j*pi/2)
388 ;; + cos(s+k*(pi/1024))*sin(j*pi/2)
390 ;; sin(s+k*pi/1024) = sin(s)*cos(k*pi/1024)
391 ;; + cos(s)*sin(k*pi/1024)
393 ;; cos(s+k*pi/1024) = cos(s)*cos(k*pi/1024)
394 ;; - sin(s)*sin(k*pi/1024)
396 (return-from sin-qd a
))
398 ;; Reduce modulo 2*pi
399 (let ((r (drem-qd a
+qd-2pi
+)))
400 ;; Now reduce by pi/2 and then by pi/1024 so that we obtain
402 (multiple-value-bind (j tmp
)
403 (divrem-qd r
+qd-pi
/2+)
404 (let* ((j (truncate (qd-0 j
)))
406 (multiple-value-bind (k tmp
)
407 (divrem-qd tmp
+qd-pi
/1024+)
408 (let* ((k (truncate (qd-0 k
)))
410 (assert (<= abs-j
2))
411 (assert (<= abs-k
256))
412 ;; Compute sin(s) and cos(s)
413 (multiple-value-bind (sin-t cos-t
)
415 (multiple-value-bind (s c
)
417 (values sin-t cos-t
))
419 ;; Compute sin(s+k*pi/1024), cos(s+k*pi/1024)
420 (let ((u (aref +qd-cos-table
+ (cl:1- abs-k
)))
421 (v (aref +qd-sin-table
+ (cl:1- abs-k
))))
423 ;; sin(s) * cos(k*pi/1024)
424 ;; + cos(s) * sin(k*pi/1024)
426 ;; cos(s) * cos(k*pi/1024)
427 ;; - sin(s) * sin(k*pi/1024)
428 (values (add-qd (mul-qd u sin-t
)
430 (sub-qd (mul-qd u cos-t
)
433 ;; sin(s) * cos(k*pi/1024)
434 ;; - cos(s) * sin(|k|*pi/1024)
436 ;; cos(s) * cos(k*pi/1024)
437 ;; + sin(s) * sin(|k|*pi/1024)
438 (values (sub-qd (mul-qd u sin-t
)
440 (add-qd (mul-qd u cos-t
)
441 (mul-qd v sin-t
))))))))
442 ;;(format t "s = ~/qd::qd-format/~%" s)
443 ;;(format t "c = ~/qd::qd-format/~%" c)
444 ;; sin(x) = sin(s+k*pi/1024) * cos(j*pi/2)
445 ;; + cos(s+k*pi/1024) * sin(j*pi/2)
447 ;; cos(j*pi/2) = 1, sin(j*pi/2) = 0
450 ;; cos(j*pi/2) = 0, sin(j*pi/2) = 1
453 ;; cos(j*pi/2) = 0, sin(j*pi/2) = -1
456 ;; cos(j*pi/2) = -1, sin(j*pi/2) = 0
461 ;; Just like sin-qd, but for cos.
462 (declare (type %quad-double a
))
463 ;; To compute sin(x), choose integers a, b so that
465 ;; x = s + a * (pi/2) + b*(pi/1024)
467 ;; with |x| <= pi/2048. Using a precomputed table of sin(k*pi/1024)
468 ;; and cos(k*pi/1024), we can compute sin(x) from sin(s) and cos(s).
470 ;; sin(x) = sin(s+k*(pi/1024) + j*pi/2)
471 ;; = sin(s+k*(pi/1024))*cos(j*pi/2)
472 ;; + cos(s+k*(pi/1024))*sin(j*pi/2)
474 ;; sin(s+k*pi/1024) = sin(s)*cos(k*pi/1024)
475 ;; + cos(s)*sin(k*pi/1024)
477 ;; cos(s+k*pi/1024) = cos(s)*cos(k*pi/1024)
478 ;; - sin(s)*sin(k*pi/1024)
480 (return-from cos-qd
+qd-one
+))
482 ;; Reduce modulo 2*pi
483 (let ((r (drem-qd a
+qd-2pi
+)))
484 ;; Now reduce by pi/2 and then by pi/1024 so that we obtain
486 (multiple-value-bind (j tmp
)
487 (divrem-qd r
+qd-pi
/2+)
488 (let* ((j (truncate (qd-0 j
)))
490 (multiple-value-bind (k tmp
)
491 (divrem-qd tmp
+qd-pi
/1024+)
492 (let* ((k (truncate (qd-0 k
)))
494 (assert (<= abs-j
2))
495 (assert (<= abs-k
256))
496 ;; Compute sin(s) and cos(s)
497 (multiple-value-bind (sin-t cos-t
)
499 (multiple-value-bind (s c
)
501 (values sin-t cos-t
))
503 ;; Compute sin(s+k*pi/1024), cos(s+k*pi/1024)
504 (let ((u (aref +qd-cos-table
+ (cl:1- abs-k
)))
505 (v (aref +qd-sin-table
+ (cl:1- abs-k
))))
507 ;; sin(s) * cos(k*pi/1024)
508 ;; + cos(s) * sin(k*pi/1024)
510 ;; cos(s) * cos(k*pi/1024)
511 ;; - sin(s) * sin(k*pi/1024)
512 (values (add-qd (mul-qd u sin-t
)
514 (sub-qd (mul-qd u cos-t
)
517 ;; sin(s) * cos(k*pi/1024)
518 ;; - cos(s) * sin(|k|*pi/1024)
520 ;; cos(s) * cos(k*pi/1024)
521 ;; + sin(s) * sin(|k|*pi/1024)
522 (values (sub-qd (mul-qd u sin-t
)
524 (add-qd (mul-qd u cos-t
)
525 (mul-qd v sin-t
))))))))
528 (format t
"s = ~/qd::qd-format/~%" s
)
529 (format t
"c = ~/qd::qd-format/~%" c
))
530 ;; sin(x) = sin(s+k*pi/1024) * cos(j*pi/2)
531 ;; + cos(s+k*pi/1024) * sin(j*pi/2)
533 ;; cos(j*pi/2) = 1, sin(j*pi/2) = 0
536 ;; cos(j*pi/2) = 0, sin(j*pi/2) = 1
539 ;; cos(j*pi/2) = 0, sin(j*pi/2) = -1
542 ;; cos(j*pi/2) = -1, sin(j*pi/2) = 0
545 ;; Compute sin and cos of a
547 (declare (type %quad-double a
))
549 (return-from sincos-qd
553 ;; Reduce modulo 2*pi
554 (let ((r (drem-qd a
+qd-2pi
+)))
555 ;; Now reduce by pi/2 and then by pi/1024 so that we obtain
557 (multiple-value-bind (j tmp
)
558 (divrem-qd r
+qd-pi
/2+)
559 (let* ((j (truncate (qd-0 j
)))
561 (multiple-value-bind (k tmp
)
562 (divrem-qd tmp
+qd-pi
/1024+)
563 (let* ((k (truncate (qd-0 k
)))
565 (assert (<= abs-j
2))
566 (assert (<= abs-k
256))
567 ;; Compute sin(s) and cos(s)
568 (multiple-value-bind (sin-t cos-t
)
570 (multiple-value-bind (s c
)
572 (values sin-t cos-t
))
574 ;; Compute sin(s+k*pi/1024), cos(s+k*pi/1024)
575 (let ((u (aref +qd-cos-table
+ (cl:1- abs-k
)))
576 (v (aref +qd-sin-table
+ (cl:1- abs-k
))))
578 ;; sin(s) * cos(k*pi/1024)
579 ;; + cos(s) * sin(k*pi/1024)
581 ;; cos(s) * cos(k*pi/1024)
582 ;; - sin(s) * sin(k*pi/1024)
583 (values (add-qd (mul-qd u sin-t
)
585 (sub-qd (mul-qd u cos-t
)
588 ;; sin(s) * cos(k*pi/1024)
589 ;; - cos(s) * sin(|k|*pi/1024)
591 ;; cos(s) * cos(k*pi/1024)
592 ;; + sin(s) * sin(|k|*pi/1024)
593 (values (sub-qd (mul-qd u sin-t
)
595 (add-qd (mul-qd u cos-t
)
596 (mul-qd v sin-t
))))))))
599 (format t
"s = ~/qd::qd-format/~%" s
)
600 (format t
"c = ~/qd::qd-format/~%" c
))
601 ;; sin(x) = sin(s+k*pi/1024) * cos(j*pi/2)
602 ;; + cos(s+k*pi/1024) * sin(j*pi/2)
604 ;; cos(j*pi/2) = 1, sin(j*pi/2) = 0
607 ;; cos(j*pi/2) = 0, sin(j*pi/2) = 1
608 (values c
(neg-qd s
)))
610 ;; cos(j*pi/2) = 0, sin(j*pi/2) = -1
611 (values (neg-qd c
) s
))
613 ;; cos(j*pi/2) = -1, sin(j*pi/2) = 0
615 (neg-qd c
))))))))))))
618 (defun atan2-qd/newton
(y x
)
619 (declare (type %quad-double y x
)
621 (optimize (speed 3) (space 0)))
622 ;; Instead of using Taylor series to compute atan, we instead use
623 ;; Newton's iteration to solve the equation
625 ;; sin(z) = y/r or cos(z) = x/r
627 ;; where r = sqrt(x^2+y^2)
631 ;; z' = z + (y - sin(z))/cos(z) (for sin)
632 ;; z' = z + (x - cos(z))/sin(z) (for cos)
634 ;; Here, x and y are normalized so that x^2 + y^2 = 1.
636 ;; If |x| > |y|, then the first iteration is used since the
637 ;; denominator is larger. Otherwise the second is used.
641 ;; Both x and y are zero. Use the signs of x and y to
642 ;; determine the result
643 (error "atan2(0,0)"))
645 ;; x = 0, but y is not. Use the sign of y.
646 (return-from atan2-qd
/newton
647 (cond ((plusp (float-sign (qd-0 y
)))
650 (neg-qd +qd-pi
/2+)))))))
653 (return-from atan2-qd
/newton
654 ;; Use the sign of x and y to figure out the result.
655 (cond ((plusp (float-sign (qd-0 x
)))
657 ((plusp (float-sign (qd-0 y
)))
660 (neg-qd +qd-pi
+))))))
663 (return-from atan2-qd
/newton
668 (when (qd-= x
(neg-qd y
))
669 (return-from atan2-qd
/newton
672 (neg-qd +qd-pi
/4+))))
674 (let* ((r (hypot-qd x y
))
679 (format t
"r = ~/qdi::qd-format/~%" r
)
680 (format t
"xx = ~/qdi::qd-format/~%" xx
)
681 (format t
"yy = ~/qdi::qd-format/~%" yy
))
683 ;; Compute double-precision approximation to atan
684 (let ((z (make-qd-d (atan (qd-0 y
) (qd-0 x
))))
687 (cond ((> (abs (qd-0 xx
))
689 ;; Newton iteration z' = z + (y - sin(z))/cos(z)
691 (multiple-value-setq (sinz cosz
) (sincos-qd z
))
692 (setf z
(add-qd z
(div-qd (sub-qd yy sinz
)
695 ;; Newton iteration z' = z - (x - cos(z))/sin(z)
697 (multiple-value-setq (sinz cosz
) (sincos-qd z
))
698 (setf z
(sub-qd z
(div-qd (sub-qd xx cosz
)
702 (defun atan-qd/newton
(y)
703 (declare (type %quad-double y
)
704 #+nil
(optimize (speed 3) (space 0)))
705 (atan2-qd/newton y
+qd-one
+))
707 (defun atan2-qd (y x
)
708 "atan2(y, x) = atan(y/x), but carefully handling the quadrant"
709 (declare (type %quad-double y x
))
710 (atan2-qd/newton y x
))
714 (declare (type %quad-double y
))
719 (declare (type %quad-double a
))
720 (atan2-qd a
(sqrt-qd (sub-d-qd 1d0
725 (declare (type %quad-double a
))
726 (atan2-qd (sqrt-qd (sub-d-qd 1d0
731 (defun tan-qd/sincos
(r)
732 (declare (type %quad-double r
))
733 (multiple-value-bind (s c
)
735 ;; What to do, what do? If C is zero, we get divide by zero
736 ;; error. We could return infinity, but quad-double stuff doesn't
737 ;; handle infinities very well.
742 (declare (type %quad-double r
))
750 (declare (type %quad-double a
))
751 ;; Hart et al. suggests sinh(x) = 1/2*(D(x) + D(x)/(D(x)+1))
752 ;; where D(x) = exp(x) - 1. This helps for x near 0.
756 ((float-infinity-p (qd-0 a
))
759 (let ((d (expm1-qd a
)))
761 (when (float-infinity-p (qd-0 d
))
762 (return-from sinh-qd d
))
763 (scale-float-qd (add-qd d
764 (div-qd d
(add-qd-d d
1d0
)))
769 (declare (type %quad-double a
))
770 ;; cosh(x) = 1/2*(exp(x)+exp(-x))
771 (let ((e (exp-qd a
)))
773 (when (float-infinity-p (qd-0 e
))
774 (return-from cosh-qd e
))
775 (scale-float-qd (add-qd e
(div-qd +qd-one
+ e
))
780 (declare (type %quad-double a
))
781 ;; Hart et al. suggests tanh(x) = D(2*x)/(2+D(2*x))
784 ((> (abs (qd-0 a
)) (/ (+ (log most-positive-double-float
)
787 ;; For this range of A, we know the answer is +/- 1.
789 ;; However, we could do better if we wanted. Assume x > 0
792 ;; tanh(x) = sinh(x)/cosh(x)
793 ;; = (1-exp(-2*x))/(1+exp(-2*x))
794 ;; = 1 - 2*exp(-2*x)/(1+exp(-2*x))
796 ;; So tanh(x) is 1 if the other term is small enough, say,
797 ;; eps. So for x large enough we can compute tanh(x) very
798 ;; accurately, thanks to how quad-double addition works.
799 ;; (The first component is, basically 1d0, and the second is
800 ;; some very small double-float.)
802 (let* ((e (exp (* -
2 a
)))
803 (res (- 1 (/ (* 2 e
) (1+ e
)))))
804 (if (minusp (float-sign (qd-0 a
)))
807 (make-qd-d (float-sign (qd-0 a
))))
809 (let* ((a2 (mul-qd-d a
2d0
))
811 (div-qd d
(add-qd-d d
2d0
))))))
815 (declare (type %quad-double a
))
816 ;; asinh(x) = log(x + sqrt(1+x^2))
818 ;; But this doesn't work well when x is small.
820 ;; log(x + sqrt(1+x^2)) = log(sqrt(1+x^2)*(1+x/sqrt(1+x^2)))
821 ;; = log(sqrt(1+x^2)) + log(1+x/sqrt(1+x^2))
822 ;; = 1/2*log(1+x^2) + log(1+x/sqrt(1+x^2))
824 ;; However that doesn't work well when x is large because x^2
827 ;; log(x + sqrt(1+x^2)) = log(x + x*sqrt(1+1/x^2))
828 ;; = log(x) + log(1+sqrt(1+1/x^2))
829 ;; = log(x) + log1p(sqrt(1+1/x^2))
832 (sqrt-qd (add-qd-d (sqr-qd a
)
834 (if (< (abs (qd-0 a
)) (sqrt most-positive-double-float
))
835 (let ((a^
2 (sqr-qd a
)))
836 (add-qd (scale-float-qd (log1p-qd a^
2) -
1)
838 (sqrt-qd (add-qd-d a^
2 1d0
))))))
840 (neg-qd (asinh-qd (neg-qd a
)))
841 (let ((1/a
(div-qd (make-qd-d 1d0
) a
)))
843 (log1p-qd (sqrt-qd (add-qd-d (sqr-qd 1/a
) 1d0
))))))))
847 (declare (type %quad-double a
))
848 ;; asinh(x) = log(x + sqrt(1+x^2))
850 ;; But this doesn't work well when x is small.
852 ;; log(x + sqrt(1+x^2)) = log(sqrt(1+x^2)*(1+x/sqrt(1+x^2)))
853 ;; = log(sqrt(1+x^2)) + log(1+x/sqrt(1+x^2))
854 ;; = 1/2*log(1+x^2) + log(1+x/sqrt(1+x^2))
856 ;; However that doesn't work well when x is large because x^2
859 ;; log(x + sqrt(1+x^2)) = log(x + x*sqrt(1+1/x^2))
860 ;; = log(x) + log(1+sqrt(1+1/x^2))
861 ;; = log(x) + log1p(sqrt(1+1/x^2))
864 (sqrt-qd (add-qd-d (sqr-qd a
)
866 (cond ((< (abs (qd-0 a
)) (sqrt most-positive-double-float
))
867 (let ((a^
2 (sqr-qd a
)))
868 (add-qd (scale-float-qd (log1p-qd a^
2) -
1)
870 (sqrt-qd (add-qd-d a^
2 1d0
)))))))
872 ((float-infinity-p (qd-0 a
))
876 (neg-qd (asinh-qd (neg-qd a
)))
877 (let ((1/a
(div-qd (make-qd-d 1d0
) a
)))
879 (log1p-qd (sqrt-qd (add-qd-d (sqr-qd 1/a
) 1d0
)))))))))
883 (declare (type %quad-double a
))
884 ;; acosh(x) = log(x + sqrt(x^2-1))
887 (sqrt-qd (sub-qd-d (sqr-qd a
)
889 ;; log(x+sqrt(x^2-1)) = log(x+sqrt((x-1)*(x+1)))
890 ;; = log(x+sqrt(x-1)*sqrt(x+1))
894 (sqrt-qd (sub-qd-d a
1d0
))
895 (sqrt-qd (add-qd-d a
1d0
)))))
897 ;; log(1 + y + sqrt(y)*sqrt(y + 2))
898 ;; = log1p(y + sqrt(y)*sqrt(y + 2))
900 ;; However, that doesn't work well if x is large.
902 ;; log(x+sqrt(x^2-1)) = log(x+x*sqrt(1-1/x^2))
903 ;; = log(x) + log(1+sqrt(1-1/x^2))
904 ;; = log(x) + log1p(sqrt(1-1/x)*sqrt(1+1/x))
906 (cond ((< (abs (qd-0 a
)) (sqrt most-positive-double-float
))
907 (let ((y (sub-qd-d a
1d0
)))
908 (log1p-qd (add-qd y
(sqrt-qd (mul-qd y
(add-qd-d y
2d0
)))))))
910 ((float-infinity-p (qd-0 a
))
913 (let ((1/a
(div-qd (make-qd-d 1d0
) a
)))
915 (log1p-qd (mul-qd (sqrt-qd (sub-d-qd 1d0
1/a
))
916 (sqrt-qd (add-d-qd 1d0
1/a
)))))))))
920 (declare (type %quad-double a
))
921 ;; atanh(x) = 1/2*log((1+x)/(1-x))
922 ;; = 1/2*log(1+(2*x)/(1-x))
923 ;; This latter expression works better for small x
925 (scale-float-qd (log-qd (div-qd (add-d-qd 1d0 a
)
928 ;; atanh(+/-1) = +/- infinity. Signal a division by zero or return
929 ;; infinity if the division-by-zero trap is disabled.
930 (if (qd-= (abs-qd a
) +qd-one
+)
931 (div-qd (make-qd-d (float-sign (qd-0 a
)))
933 (scale-float-qd (log1p-qd (div-qd (scale-float-qd a
1)
938 (defun random-qd (&optional
(state *random-state
*))
939 "Generate a quad-double random number in the range [0,1)"
940 (declare (optimize (speed 3)))
941 ;; Strategy: Generate 31 bits at a time, shift the bits and repeat 7 times.
943 (m-const (scale-float 1d0 -
31))
945 (declare (type %quad-double r
)
946 (double-float m-const m
))
948 (let ((d (cl:* m
(random #x7fffffff state
))))
949 (setf r
(add-qd-d r d
))
950 (setf m
(cl:* m m-const
))))