[armedbear-cvs] r11729 - trunk/abcl/src/org/armedbear/lisp

Erik Huelsmann ehuelsmann at common-lisp.net
Sat Apr 4 21:57:58 UTC 2009


Author: ehuelsmann
Date: Sat Apr  4 17:57:58 2009
New Revision: 11729

Log:
Fix EXPT with a Bignum exponent.
At the same time, simplify EXPT and add some documentation.


Modified:
   trunk/abcl/src/org/armedbear/lisp/MathFunctions.java

Modified: trunk/abcl/src/org/armedbear/lisp/MathFunctions.java
==============================================================================
--- trunk/abcl/src/org/armedbear/lisp/MathFunctions.java	(original)
+++ trunk/abcl/src/org/armedbear/lisp/MathFunctions.java	Sat Apr  4 17:57:58 2009
@@ -747,60 +747,27 @@
             }
             if (base.zerop())
                 return base;
-            if (power instanceof Fixnum) {
-                if (base.rationalp())
-                    return intexp(base, power);
-                LispObject result;
-                if (base instanceof SingleFloat)
-                    result = SingleFloat.ONE;
-                else if (base instanceof DoubleFloat)
-                    result = DoubleFloat.ONE;
-                else
-                    // base is complex
-                    result = Fixnum.ONE;
-                int pow = ((Fixnum)power).value;
-                if (pow > 0) {
-                    LispObject term = base;
-                    while (pow != 0) {
-                        if ((pow & 1) == 1)
-                           result = result.multiplyBy(term);
-
-                        term = term.multiplyBy(term);
-                        pow = pow >> 1;
-                    }
-                } else if (pow < 0) {
-                    LispObject term = base;
-                    pow = -pow;
-                    while (pow != 0) {
-                        if ((pow & 1) == 1)
-                           result = result.divideBy(term);
-
-                        term = term.multiplyBy(term);
-                        pow = pow >> 1;
-                    }
-                }
-                if (TRAP_OVERFLOW) {
-                    if (result instanceof SingleFloat)
-                        if (Float.isInfinite(((SingleFloat)result).value))
-                            return error(new FloatingPointOverflow(NIL));
-                    if (result instanceof DoubleFloat)
-                        if (Double.isInfinite(((DoubleFloat)result).value))
-                            return error(new FloatingPointOverflow(NIL));
-                }
-                if (TRAP_UNDERFLOW) {
-                    if (result.zerop())
-                        return error(new FloatingPointUnderflow(NIL));
-                }
-                return result;
+            if (base.isEqualTo(1))
+                return base;
+            
+            if ((power instanceof Fixnum
+                 || power instanceof Bignum)
+                 && (base.rationalp()
+                     || (base instanceof Complex
+                         && ((Complex)base).realpart.rationalp()))) {
+                // exact math version
+                return intexp(base, power);
             }
-            if (base instanceof Fixnum && power instanceof Bignum)
-                return ((Fixnum)base).pow(power);
+            // for anything not a rational or complex rational, use
+            // float approximation.
             if (base instanceof Complex || power instanceof Complex)
                 return exp(power.multiplyBy(log(base)));
             final double x; // base
             final double y; // power
             if (base instanceof Fixnum)
                 x = ((Fixnum)base).value;
+            else if (base instanceof Bignum)
+                x = ((Bignum)base).doubleValue();
             else if (base instanceof Ratio)
                 x = ((Ratio)base).doubleValue();
             else if (base instanceof SingleFloat)
@@ -810,7 +777,12 @@
             else
                 return error(new LispError("EXPT: unsupported case: base is of type " +
                                             base.typeOf().writeToString()));
-            if (power instanceof Ratio)
+
+            if (power instanceof Fixnum)
+                y = ((Fixnum)power).value;
+            else if (power instanceof Bignum)
+                y = ((Bignum)power).doubleValue();
+            else if (power instanceof Ratio)
                 y = ((Ratio)power).doubleValue();
             else if (power instanceof SingleFloat)
                 y = ((SingleFloat)power).value;
@@ -826,30 +798,76 @@
                     double realPart = r * Math.cos(y * Math.PI);
                     double imagPart = r * Math.sin(y * Math.PI);
                     if (base instanceof DoubleFloat || power instanceof DoubleFloat)
-                        return Complex.getInstance(new DoubleFloat(realPart),
-                                                   new DoubleFloat(imagPart));
+                        return Complex
+                            .getInstance(OverUnderFlowCheck(new DoubleFloat(realPart)),
+                                         OverUnderFlowCheck(new DoubleFloat(imagPart)));
                     else
-                        return Complex.getInstance(new SingleFloat((float)realPart),
-                                                   new SingleFloat((float)imagPart));
+                        return Complex
+                            .getInstance(OverUnderFlowCheck(new SingleFloat((float)realPart)),
+                                         OverUnderFlowCheck(new SingleFloat((float)imagPart)));
                 }
             }
             if (base instanceof DoubleFloat || power instanceof DoubleFloat)
-                return new DoubleFloat(r);
+                return OverUnderFlowCheck(new DoubleFloat(r));
             else
-                return new SingleFloat((float)r);
+                return OverUnderFlowCheck(new SingleFloat((float)r));
         }
     };
 
+    /** Checks number for over- or underflow values.
+     *
+     * @param number
+     * @return number or signals an appropriate error
+     * @throws org.armedbear.lisp.ConditionThrowable
+     */
+    private final static LispObject OverUnderFlowCheck(LispObject number)
+            throws ConditionThrowable
+    {
+        if (number instanceof Complex) {
+            OverUnderFlowCheck(((Complex)number).realpart);
+            OverUnderFlowCheck(((Complex)number).imagpart);
+            return number;
+        }
+
+        if (TRAP_OVERFLOW) {
+            if (number instanceof SingleFloat)
+                if (Float.isInfinite(((SingleFloat)number).value))
+                    return error(new FloatingPointOverflow(NIL));
+            if (number instanceof DoubleFloat)
+                if (Double.isInfinite(((DoubleFloat)number).value))
+                    return error(new FloatingPointOverflow(NIL));
+        }
+        if (TRAP_UNDERFLOW) {
+            if (number.zerop())
+                return error(new FloatingPointUnderflow(NIL));
+        }
+        return number;
+    }
+
     // Adapted from SBCL.
+    /** Return the exponent of base taken to the integer exponent power
+     *
+     * @param base A value of any type
+     * @param power An integer (fixnum or bignum) value
+     * @throws org.armedbear.lisp.ConditionThrowable
+     */
     private static final LispObject intexp(LispObject base, LispObject power)
         throws ConditionThrowable
     {
+        if (power.isEqualTo(0))
+            return Fixnum.ONE;
+        if (base.isEqualTo(1))
+            return base;
+        if (base.isEqualTo(0))
+            return base;
+
         if (power.minusp()) {
             power = Fixnum.ZERO.subtract(power);
             return Fixnum.ONE.divideBy(intexp(base, power));
         }
         if (base.eql(Fixnum.TWO))
             return Fixnum.ONE.ash(power);
+
         LispObject nextn = power.ash(Fixnum.MINUS_ONE);
         LispObject total;
         if (power.oddp())
@@ -860,10 +878,10 @@
             if (nextn.zerop())
                 return total;
             base = base.multiplyBy(base);
-            power = nextn;
-            nextn = power.ash(Fixnum.MINUS_ONE);
-            if (power.oddp())
+
+            if (nextn.oddp())
                 total = base.multiplyBy(total);
+            nextn = nextn.ash(Fixnum.MINUS_ONE);
         }
     }
 }




More information about the armedbear-cvs mailing list