4 ;;------------------------------------------------------------------------
5 ;; Previously from share/contrib/floatproperties.lisp
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
)))
43 "Floating-point epsilon, basically the smallest value eps such that
44 1+eps is not equal to 1"
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."
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."
62 (defmfun $float_precision
(f)
63 "The number of bits of precision in the floating-point number F. This
64 includes floats and bigfloats."
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
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
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
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
)
95 (+ expo
(float-digits 1d0
) -
1)))
96 "The smallest exponent that decode-float can return for a normalized
99 (defmfun $unit_in_last_place
(f)
100 (cond ((integerp f
) 1)
105 $least_positive_float
)
107 (multiple-value-bind (significand expon
)
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
115 (if (> expon
+most-negative-normalized-float-exponent
+)
119 (if ($is_power_of_two significand
)
122 $least_positive_float
)))))
124 (let ((significand (cadr f
))
125 (expon (- (caddr f
) (bigfloat-prec f
))))
126 (cond ((= 0 significand
)
127 ; ULP is arbitrarily small for bigfloat 0
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
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")
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)
146 (= 0 (logand (abs n
) (+ (abs n
) -
1)))))
149 (= 0.5 (decode-float n
))))
151 ($is_power_of_two
(cadr 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")
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
)
165 (merror (intl:gettext
"scale_float: second arg must be an integer: ~M~%")
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~%")
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)
190 (multiple-value-bind (mant expo sign
)
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.
197 (setf sign
(float-sign f
))
203 (multiple-value-bind (mant expo sign
)
204 (bigfloat:decode-float
(bigfloat:bigfloat f
))
206 (to (bigfloat:* mant
2))
210 (merror (intl:gettext
"decode_float is only defined for floats and bfloats: ~M")
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
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
226 (defmfun $integer_decode_float
(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.
244 (multiple-value-bind (int expo sign
)
245 (bigfloat:integer-decode-float
(bigfloat:bigfloat f
))
251 (merror (intl:gettext
"decode_float is only defined for floats and bfloats: ~M")
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
263 (to (bigfloat:float-sign
(bigfloat:bigfloat f
))))
265 (merror (intl:gettext
"float_sign is only defined for floats and bfloats: ~M")
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
)))