Fix some issues with make dist-gzip
[maxima.git] / src / float-properties.lisp
blob9f4fb65add8c35012f055e34b73b4078192e6b2e
1 ;;;; Float properties
2 (in-package "MAXIMA")
4 ;;------------------------------------------------------------------------
5 ;; Previously from share/contrib/floatproperties.lisp
6 ;;
7 ;; Expose some properties of floating-point numbers to Maxima.
8 (defmvar $most_positive_float +most-positive-flonum+
9 "Largest positive floating-point number"
10 :properties ((assign 'neverset)))
12 (defmvar $most_negative_float +most-negative-flonum+
13 "Most negative floating-point number"
14 :properties ((assign 'neverset)))
16 ;; largest_float and largest_negative_float are deprecated constants.
17 (defmvar $largest_float $most_positive_float
18 "Deprecated. Use most_positive_float"
19 :deprecated-p "Use most_positive_float.")
21 (defmvar $largest_negative_float $most_negative_float
22 "Deprecated. Use most_negative_float"
23 :deprecated-p "Use most_negative_float.")
25 (eval-when (:load-toplevel :compile-toplevel :execute)
26 (defmvar $least_positive_float +least-positive-flonum+
27 "The smallest positive floating-point number"
28 :properties ((assign 'neverset)))
30 (defmvar $least_positive_normalized_float +least-positive-normalized-flonum+
31 "The smallest positive normalized floating-point number"
32 :properties ((assign 'neverset))))
34 (defmvar $least_negative_float +least-negative-flonum+
35 "The least negative floating-point number"
36 :properties ((assign 'neverset)))
38 (defmvar $least_negative_normalized_float +least-negative-normalized-flonum+
39 "The least negative normalized floating-point number"
40 :properties ((assign 'neverset)))
42 (defun $float_eps ()
43 "Floating-point epsilon, basically the smallest value eps such that
44 1+eps is not equal to 1"
45 +flonum-epsilon+)
47 (defun $bigfloat_eps ()
48 "The bigfloat version of float_eps; the smallest bigfloat such that
49 1+eps is not equal to 1."
50 (let ((r ($bfloat (div 1 (expt 2 fpprec)))))
51 (list (first r) (incf (second r)) (third r))))
53 (defmfun ($float_bits :deprecated-p $float_precision) ()
54 "The number of bits in the fraction part of a floating-point number."
55 (float-digits 0d0))
57 (defmfun ($bigfloat_bits :deprecated-p $float_precision) ()
58 "The number of bits in the fraction part of a bigfloat number. Note
59 that this changes when $fpprec is changed, of course."
60 fpprec)
62 (defmfun $float_precision (f)
63 "The number of bits of precision in the floating-point number F. This
64 includes floats and bigfloats."
65 (cond ((floatp f)
66 (float-precision f))
67 (($bfloatp f)
68 (bigfloat-prec f))
70 (merror (intl:gettext "~M: expected a float or bigfloat number: ~M")
71 '$float_precision f))))
74 ;;; ULP is the Unit in the Last Place
76 ;;; ---Definition---
77 ;;; unit_in_last_place (ulp) is the gap between x and the nearest other number
78 ;;; -- https://people.eecs.berkeley.edu/~wkahan/LOG10HAF.TXT
79 ;;;
80 ;;; For floating-point exact powers of two, the ulp is the gap to the next
81 ;;; number down in magnitude, which is smaller than the gap to the next number up:
82 ;;; ulp(0.9999) = ulp(1.0) = ulp(1.0001)/2
83 ;;; The ULP of all denormalized numbers is the same:
84 ;;; ulp(least_positive_float) = ulp(least_positive_float*2^53) = least_positive_float
85 ;;;
86 ;;; We assume that there is only one least-positive-flonum, so these functions
87 ;;; will not work for denormalized single-precision floats for example.
88 (defconstant +most-negative-normalized-float-exponent+
89 (let ((expo (nth-value 1 (decode-float $least_positive_normalized_float))))
90 ;; Some lisps may not have support for denormals, like Clisp. In
91 ;; that case set the exponent to be the exponent of the smallest
92 ;; float, add the number of fraction bits, and subtract 1.
93 (if (/= $least_positive_float $least_positive_normalized_float)
94 expo
95 (+ expo (float-digits 1d0) -1)))
96 "The smallest exponent that decode-float can return for a normalized
97 number.")
99 (defmfun $unit_in_last_place (f)
100 (cond ((integerp f) 1)
101 ((ratnump f) 0)
102 ((floatp f)
103 (cond
104 ((= f 0.0)
105 $least_positive_float)
107 (multiple-value-bind (significand expon)
108 (decode-float f)
109 ;; If the exponent is smaller than the smallest
110 ;; normalized exponent, the ULP is the smallest float.
111 ;; Otherwise, the ULP has an exponent that is
112 ;; float-digits smaller, except when the fraction is a
113 ;; power of two, where we have to increase the exponent
114 ;; by 1.
115 (if (> expon +most-negative-normalized-float-exponent+)
116 (scale-float 0.5d0
117 (- expon
118 (float-digits f)
119 (if ($is_power_of_two significand)
121 -1)))
122 $least_positive_float)))))
123 (($bfloatp f)
124 (let ((significand (cadr f))
125 (expon (- (caddr f) (bigfloat-prec f))))
126 (cond ((= 0 significand)
127 ; ULP is arbitrarily small for bigfloat 0
128 bigfloatzero)
129 ;; precision of resulting bigfloat not necessarily the same as input
130 ;; but that doesn't matter, since 2^n can be represented exactly in all
131 ;; precisions
133 (exptbigfloat ($bfloat 2)
134 (if ($is_power_of_two (abs significand))
135 (+ expon -1) expon))))))
137 (merror (intl:gettext "~:@M: unit_in_last_place is not defined")
138 f))))
140 ;;; is_power_of_two works for explicit numbers: integers, floats, bfloats, rats
141 ;;; NOTE: a negative number is not a power of 2
142 ;;; does NOT handle expressions (by choice)
143 (defmfun $is_power_of_two (n)
144 (cond ((integerp n)
145 (and (> n 0)
146 (= 0 (logand (abs n) (+ (abs n) -1)))))
147 ((floatp n)
148 (and (> n 0.0)
149 (= 0.5 (decode-float n))))
150 (($bfloatp n)
151 ($is_power_of_two (cadr n)))
152 ((ratnump n)
153 ;; ratnums not needed for unit_in_last_place, but let's be complete
154 (and (= (cadr n) 1) ($is_power_of_two (caddr n))))
156 (merror (intl:gettext "~:@M: is_power_of_two is only defined for numbers")
157 n))))
159 ;; $scale_float(f, n)
161 ;; A Maxima interface to CL:SCALE-FLOAT. Basically compuutes f *
162 ;; 2^n, but this should be exact.
163 (defmfun $scale_float (f n)
164 (unless (integerp n)
165 (merror (intl:gettext "scale_float: second arg must be an integer: ~M~%")
167 (cond
168 ((floatp f)
169 (scale-float f n))
170 (($bfloatp f)
171 ;; Should probably diddle the bfloat parts directly, but it's
172 ;; easier to use bigfloat:scale-float.
173 (to (bigfloat:scale-float (bigfloat:bigfloat f) n)))
175 (merror (intl:gettext "scale_float: first arg must be a float or bfloat: ~M~%")
176 f))))
178 ;; $decode_float(f)
180 ;; A Maxima interface to CL:DECODE-FLOAT which returns a list of a
181 ;; float, exponent, and a sign such that (* sign (scale-float float
182 ;; exponent)) is exactly the same number.
184 ;; We differ from CL:DECODE-FLOAT in that Except we return a mantissa
185 ;; in the range [1, 2) instead of [0.5,1) because that's what IEEE
186 ;; mantissas are. The exponent is adjusted appropriately.
187 (defmfun $decode_float (f)
188 (cond
189 ((floatp f)
190 (multiple-value-bind (mant expo sign)
191 (cl:decode-float f)
192 ;; Ecl 21.2.1 has a broken decode-float for negative numbers.
193 ;; It returns 0 for the sign instead of -1. Just unconditional
194 ;; set the sign via float-sign. Ecl supports signed zeroes, so
195 ;; float-sign is easiest way to get the correct sign.
196 #+ecl
197 (setf sign (float-sign f))
198 (list '(mlist)
199 (* 2 mant)
200 (1- expo)
201 sign)))
202 (($bfloatp f)
203 (multiple-value-bind (mant expo sign)
204 (bigfloat:decode-float (bigfloat:bigfloat f))
205 (list '(mlist)
206 (to (bigfloat:* mant 2))
207 (1- expo)
208 (to sign))))
210 (merror (intl:gettext "decode_float is only defined for floats and bfloats: ~M")
211 f))))
213 ;; $integer_decode_float(f)
215 ;; A Maxima interface to CL:INTEGER-DECODE-FLOAT which returns a
216 ;; list of an integer, an exponent and a sign such that (* sign
217 ;; (scale-float (float integer 1d0) exponent)) returns the original
218 ;; number.
220 ;; However, there's quite a bit of variety in Lisps when returning a
221 ;; value for denormals (if supported). We don't want to expose this
222 ;; to Maxima, so we choose to decide how to handle that. Thus, the
223 ;; number of bits in the integer part is the same as (float-precision
224 ;; f). If not, we scale the integer appropriately and the exponent as
225 ;; well.
226 (defmfun $integer_decode_float (f)
227 (cond
228 ((floatp f)
229 (multiple-value-bind (int expo sign)
230 (integer-decode-float f)
231 (let ((shift (- (integer-length int) (float-precision f))))
232 ;; If shift > 0, we have a denormal and want to reduce the
233 ;; number of bits in the integer part.
234 (if (plusp shift)
235 (list '(mlist)
236 (ash int (- shift))
237 (+ expo shift)
238 sign)
239 (list '(mlist)
241 expo
242 sign)))))
243 (($bfloatp f)
244 (multiple-value-bind (int expo sign)
245 (bigfloat:integer-decode-float (bigfloat:bigfloat f))
246 (list '(mlist)
248 expo
249 sign)))
251 (merror (intl:gettext "decode_float is only defined for floats and bfloats: ~M")
252 f))))
254 (defmfun $float_sign (f)
255 "The sign of the float F, which can either be a float or bigfloat.
256 The sign is +1 or -1 depending on the sign of F. It has the same
257 type as F. Note: some lisps do not support signed floating-point
258 zeroes."
259 (cond
260 ((floatp f)
261 (float-sign f))
262 (($bfloatp f)
263 (to (bigfloat:float-sign (bigfloat:bigfloat f))))
265 (merror (intl:gettext "float_sign is only defined for floats and bfloats: ~M")
266 f))))
268 (defmfun $float_infinity_p (x)
269 (and (typep x 'double-float) (or (> x +most-positive-flonum+) (< x +most-negative-flonum+))))
271 (defmfun $float_nan_p (x)
272 (and (typep x 'double-float) (/= x x)))