source: trunk/abcl/src/org/armedbear/lisp/FloatFunctions.java

Last change on this file was 15292, checked in by Mark Evenson, 4 years ago

Further fixes for floating point values

DECODE-FLOAT now returns a significand in the interval between 1/2
(inclusive) and 1 (exclusive) as implied by ANSI.

Coercion of values smaller than 2-1023 to double floats no longer
returns zero.

Completed addressing the issues raised by Robert Dodier in
<https://github.com/armedbear/abcl/issues/93>,
<https://github.com/armedbear/abcl/issues/94>, and
<https://github.com/armedbear/abcl/issues/95>.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.9 KB
Line 
1/*
2 * FloatFunctions.java
3 *
4 * Copyright (C) 2003-2006 Peter Graves
5 * $Id: FloatFunctions.java 15292 2020-06-01 13:56:48Z mevenson $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38import java.math.BigInteger;
39
40public final class FloatFunctions
41{
42    // ### set-floating-point-modes &key traps => <no values>
43    private static final Primitive SET_FLOATING_POINT_MODES =
44        new Primitive("set-floating-point-modes", PACKAGE_EXT, true,
45                      "&key traps")
46    {
47        @Override
48        public LispObject execute(LispObject[] args)
49        {
50            if (args.length % 2 != 0)
51                program_error("Odd number of keyword arguments.");
52            for (int i = 0; i < args.length; i += 2) {
53                LispObject key = checkSymbol(args[i]);
54                LispObject value = args[i+1];
55                if (key == Keyword.TRAPS) {
56                    boolean trap_overflow  = false;
57                    boolean trap_underflow = false;
58                    while (value != NIL) {
59                        LispObject car = value.car();
60                        if (car == Keyword.OVERFLOW)
61                            trap_overflow = true;
62                        else if (car == Keyword.UNDERFLOW)
63                            trap_underflow = true;
64                        else
65                            error(new LispError("Unsupported floating point trap: " +
66                                                 car.princToString()));
67                        value = value.cdr();
68                    }
69                    TRAP_OVERFLOW  = trap_overflow;
70                    TRAP_UNDERFLOW = trap_underflow;
71                } else
72                    error(new LispError("Unrecognized keyword: " + key.princToString()));
73            }
74            return LispThread.currentThread().nothing();
75        }
76    };
77
78    // ### get-floating-point-modes => modes
79    private static final Primitive GET_FLOATING_POINT_MODES =
80        new Primitive("get-floating-point-modes", PACKAGE_EXT, true, "")
81    {
82        @Override
83        public LispObject execute()
84        {
85            LispObject traps = NIL;
86            if (TRAP_UNDERFLOW)
87                traps = traps.push(Keyword.UNDERFLOW);
88            if (TRAP_OVERFLOW)
89                traps = traps.push(Keyword.OVERFLOW);
90            return list(Keyword.TRAPS, traps);
91        }
92    };
93
94    // ### integer-decode-float float => significand, exponent, integer-sign
95    private static final Primitive INTEGER_DECODE_FLOAT =
96        new Primitive("integer-decode-float", "float")
97    {
98        @Override
99        public LispObject execute(LispObject arg)
100        {
101            if (arg instanceof SingleFloat) {
102                if (arg.equals(SingleFloat.SINGLE_FLOAT_POSITIVE_INFINITY)
103                    || arg.equals(SingleFloat.SINGLE_FLOAT_NEGATIVE_INFINITY)) {
104                    return error(new LispError("Cannot decode infinity."));
105                }
106                int bits =
107                    Float.floatToRawIntBits(((SingleFloat)arg).value);
108                int s = ((bits >> 31) == 0) ? 1 : -1;
109                int e = (int) ((bits >> 23) & 0xffL);
110                int m;
111                if (e == 0)
112                    m = (bits & 0x7fffff) << 1;
113                else
114                    m = (bits & 0x7fffff) | 0x800000;
115                LispObject significand = number(m);
116                Fixnum exponent = Fixnum.getInstance(e - 150);
117                Fixnum sign = Fixnum.getInstance(s);
118                return LispThread.currentThread().setValues(significand,
119                                                            exponent,
120                                                            sign);
121            }
122            if (arg instanceof DoubleFloat) {
123                if (arg.equals(DoubleFloat.DOUBLE_FLOAT_POSITIVE_INFINITY)
124                    || arg.equals(DoubleFloat.DOUBLE_FLOAT_NEGATIVE_INFINITY)) {
125                    return error(new LispError("Cannot decode infinity."));
126                }
127
128                long bits =
129                    Double.doubleToRawLongBits((double)((DoubleFloat)arg).value);
130                int s = ((bits >> 63) == 0) ? 1 : -1;
131                int e = (int) ((bits >> 52) & 0x7ffL);
132                long m;
133                if (e == 0)
134                    m = (bits & 0xfffffffffffffL) << 1;
135                else
136                    m = (bits & 0xfffffffffffffL) | 0x10000000000000L;
137                LispObject significand = number(m);
138                Fixnum exponent = Fixnum.getInstance(e - 1075);
139                Fixnum sign = Fixnum.getInstance(s);
140                return LispThread.currentThread().setValues(significand,
141                                                            exponent,
142                                                            sign);
143            }
144            return type_error(arg, Symbol.FLOAT);
145        }
146    };
147
148    // ### %float-bits float => integer
149    private static final Primitive _FLOAT_BITS =
150        new Primitive("%float-bits", PACKAGE_SYS, true, "integer")
151    {
152        @Override
153        public LispObject execute(LispObject arg)
154        {
155            if (arg instanceof SingleFloat) {
156                int bits = Float.floatToIntBits(((SingleFloat)arg).value);
157                BigInteger big = BigInteger.valueOf(bits >> 1);
158                return Bignum.getInstance(big.shiftLeft(1).add(((bits & 1) == 1) ? BigInteger.ONE : BigInteger.ZERO));
159            }
160            if (arg instanceof DoubleFloat) {
161                long bits = Double.doubleToLongBits(((DoubleFloat)arg).value);
162                BigInteger big = BigInteger.valueOf(bits >> 1);
163                return Bignum.getInstance(big.shiftLeft(1).add(((bits & 1) == 1) ? BigInteger.ONE : BigInteger.ZERO));
164            }
165            return type_error(arg, Symbol.FLOAT);
166        }
167    };
168
169    // ### rational
170    private static final Primitive RATIONAL =
171        new Primitive("rational", "number")
172    {
173        @Override
174        public LispObject execute(LispObject arg)
175        {
176            if (arg instanceof SingleFloat)
177                return ((SingleFloat)arg).rational();
178            if (arg instanceof DoubleFloat)
179                return ((DoubleFloat)arg).rational();
180            if (arg.rationalp())
181                return arg;
182            return type_error(arg, Symbol.REAL);
183        }
184    };
185
186    // ### float-radix
187    // float-radix float => float-radix
188    private static final Primitive FLOAT_RADIX =
189        new Primitive("float-radix", "float")
190    {
191        @Override
192        public LispObject execute(LispObject arg)
193        {
194            if (arg instanceof SingleFloat || arg instanceof DoubleFloat)
195                return Fixnum.TWO;
196            return type_error(arg, Symbol.FLOAT);
197        }
198    };
199
200    static final Fixnum FIXNUM_24 = Fixnum.getInstance(24);
201    static final Fixnum FIXNUM_53 = Fixnum.getInstance(53);
202
203    // ### float-digits
204    // float-digits float => float-digits
205    private static final Primitive FLOAT_DIGITS =
206        new Primitive("float-digits", "float")
207    {
208        @Override
209        public LispObject execute(LispObject arg)
210        {
211            if (arg instanceof SingleFloat)
212                return FIXNUM_24;
213            if (arg instanceof DoubleFloat)
214                return FIXNUM_53;
215            return type_error(arg, Symbol.FLOAT);
216        }
217    };
218
219    // ### scale-float float integer => scaled-float
220    private static final Primitive SCALE_FLOAT =
221        new Primitive("scale-float", "float integer")
222    {
223        @Override
224        public LispObject execute(LispObject first, LispObject second)
225
226        {
227            if (first instanceof SingleFloat) {
228                float f = ((SingleFloat)first).value;
229                int n = Fixnum.getValue(second);
230                return new SingleFloat(f * (float) Math.pow(2, n));
231            }
232            if (first instanceof DoubleFloat) {
233                double d = ((DoubleFloat)first).value;
234                int n = Fixnum.getValue(second);
235                return new DoubleFloat(d * Math.pow(2, n));
236            }
237            return type_error(first, Symbol.FLOAT);
238        }
239    };
240
241    // ### coerce-to-single-float
242    private static final Primitive COERCE_TO_SINGLE_FLOAT =
243        new Primitive("coerce-to-single-float", PACKAGE_SYS, false)
244    {
245        @Override
246        public LispObject execute(LispObject arg)
247        {
248            return SingleFloat.coerceToFloat(arg);
249        }
250    };
251
252    // ### coerce-to-double-float
253    private static final Primitive COERCE_TO_DOUBLE_FLOAT =
254        new Primitive("coerce-to-double-float", PACKAGE_SYS, false)
255    {
256        @Override
257        public LispObject execute(LispObject arg)
258        {
259            return DoubleFloat.coerceToFloat(arg);
260        }
261    };
262
263    // ### float
264    // float number &optional prototype => float
265    private static final Primitive FLOAT =
266        new Primitive("float", "number &optional prototype")
267    {
268        @Override
269        public LispObject execute(LispObject arg)
270        {
271            if (arg instanceof SingleFloat || arg instanceof DoubleFloat)
272                return arg;
273            return SingleFloat.coerceToFloat(arg);
274        }
275        @Override
276        public LispObject execute(LispObject first, LispObject second)
277
278        {
279            if (second instanceof SingleFloat)
280                return SingleFloat.coerceToFloat(first);
281            if (second instanceof DoubleFloat)
282                return DoubleFloat.coerceToFloat(first);
283            return type_error(second, Symbol.FLOAT);
284        }
285    };
286
287    // ### floatp
288    // floatp object => generalized-boolean
289    private static final Primitive FLOATP = new Primitive("floatp", "object")
290    {
291        @Override
292        public LispObject execute(LispObject arg)
293        {
294            if (arg instanceof SingleFloat)
295                return T;
296            if (arg instanceof DoubleFloat)
297                return T;
298            return NIL;
299        }
300    };
301
302    // ### single-float-bits
303    private static final Primitive SINGLE_FLOAT_BITS =
304        new Primitive("single-float-bits", PACKAGE_SYS, true, "float")
305    {
306        @Override
307        public LispObject execute(LispObject arg)
308        {
309            if (arg instanceof SingleFloat) {
310                SingleFloat f = (SingleFloat) arg;
311                return Fixnum.getInstance(Float.floatToIntBits(f.value));
312            }
313            return type_error(arg, Symbol.FLOAT);
314        }
315    };
316
317    // ### double-float-high-bits
318    private static final Primitive DOUBLE_FLOAT_HIGH_BITS =
319        new Primitive("double-float-high-bits", PACKAGE_SYS, true, "float")
320    {
321        @Override
322        public LispObject execute(LispObject arg)
323        {
324            if (arg instanceof DoubleFloat) {
325                DoubleFloat f = (DoubleFloat) arg;
326                return number(Double.doubleToLongBits(f.value) >>> 32);
327            }
328            return type_error(arg, Symbol.DOUBLE_FLOAT);
329        }
330    };
331
332    // ### double-float-low-bits
333    private static final Primitive DOUBLE_FLOAT_LOW_BITS =
334        new Primitive("double-float-low-bits", PACKAGE_SYS, true, "float")
335    {
336        @Override
337        public LispObject execute(LispObject arg)
338        {
339            if (arg instanceof DoubleFloat) {
340                DoubleFloat f = (DoubleFloat) arg;
341                return number(Double.doubleToLongBits(f.value) & 0xffffffffL);
342            }
343            return type_error(arg, Symbol.DOUBLE_FLOAT);
344        }
345    };
346
347    // ### make-single-float bits => float
348    private static final Primitive MAKE_SINGLE_FLOAT =
349        new Primitive("make-single-float", PACKAGE_SYS, true, "bits")
350    {
351        @Override
352        public LispObject execute(LispObject arg)
353
354        {
355            if (arg instanceof Fixnum) {
356                int bits = ((Fixnum)arg).value;
357                return new SingleFloat(Float.intBitsToFloat(bits));
358            }
359            if (arg instanceof Bignum) {
360                long bits = ((Bignum)arg).value.longValue();
361                return new SingleFloat(Float.intBitsToFloat((int)bits));
362            }
363            return type_error(arg, Symbol.INTEGER);
364        }
365    };
366
367    // ### make-double-float bits => float
368    private static final Primitive MAKE_DOUBLE_FLOAT =
369        new Primitive("make-double-float", PACKAGE_SYS, true, "bits")
370    {
371        @Override
372        public LispObject execute(LispObject arg)
373
374        {
375            if (arg instanceof Fixnum) {
376                long bits = (long) ((Fixnum)arg).value;
377                return new DoubleFloat(Double.longBitsToDouble(bits));
378            }
379            if (arg instanceof Bignum) {
380                long bits = ((Bignum)arg).value.longValue();
381                return new DoubleFloat(Double.longBitsToDouble(bits));
382            }
383            return type_error(arg, Symbol.INTEGER);
384        }
385    };
386
387    // ### float-infinity-p
388    private static final Primitive FLOAT_INFINITY_P =
389        new Primitive("float-infinity-p", PACKAGE_SYS, true)
390    {
391        @Override
392        public LispObject execute(LispObject arg)
393
394        {
395            if (arg instanceof SingleFloat)
396                return Float.isInfinite(((SingleFloat)arg).value) ? T : NIL;
397            if (arg instanceof DoubleFloat)
398                return Double.isInfinite(((DoubleFloat)arg).value) ? T : NIL;
399            return type_error(arg, Symbol.FLOAT);
400        }
401    };
402
403    // ### float-nan-p
404    private static final Primitive FLOAT_NAN_P =
405        new Primitive("float-nan-p", PACKAGE_SYS, true)
406    {
407        @Override
408        public LispObject execute(LispObject arg)
409
410        {
411            if (arg instanceof SingleFloat)
412                return Float.isNaN(((SingleFloat)arg).value) ? T : NIL;
413            if (arg instanceof DoubleFloat)
414                return Double.isNaN(((DoubleFloat)arg).value) ? T : NIL;
415            return type_error(arg, Symbol.FLOAT);
416        }
417    };
418
419    // ### float-string
420    private static final Primitive FLOAT_STRING =
421        new Primitive("float-string", PACKAGE_SYS, true)
422    {
423        @Override
424        public LispObject execute(LispObject arg)
425        {
426            final String s1;
427            if (arg instanceof SingleFloat)
428                s1 = String.valueOf(((SingleFloat)arg).value);
429            else if (arg instanceof DoubleFloat)
430                s1 = String.valueOf(((DoubleFloat)arg).value);
431            else
432                return type_error(arg, Symbol.FLOAT);
433            int i = s1.indexOf('E');
434            if (i < 0)
435                return new SimpleString(s1);
436            String s2 = s1.substring(0, i);
437            int exponent = Integer.parseInt(s1.substring(i + 1));
438            if (exponent == 0)
439                return new SimpleString(s2);
440            int index = s2.indexOf('.');
441            if (index < 0)
442                return new SimpleString(s2);
443            StringBuffer sb = new StringBuffer(s2);
444            if (index >= 0)
445                sb.deleteCharAt(index);
446            // Now we've got just the digits in the StringBuffer.
447            if (exponent > 0) {
448                int newIndex = index + exponent;
449                if (newIndex < sb.length())
450                    sb.insert(newIndex, '.');
451                else if (newIndex == sb.length())
452                    sb.append('.');
453                else {
454                    // We need to add some zeros.
455                    while (newIndex > sb.length())
456                        sb.append('0');
457                    sb.append('.');
458                }
459            } else {
460                Debug.assertTrue(exponent < 0);
461                int newIndex = index + exponent;
462                while (newIndex < 0) {
463                    sb.insert(0, '0');
464                    ++newIndex;
465                }
466                sb.insert(0, '.');
467            }
468            return new SimpleString(sb.toString());
469        }
470    };
471}
Note: See TracBrowser for help on using the repository browser.