KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > groovy > runtime > NumberMath


1 /*
2  * Created on Mar 7, 2004
3  *
4  */

5 package org.codehaus.groovy.runtime;
6
7 import java.math.BigDecimal JavaDoc;
8 import java.math.BigInteger JavaDoc;
9
10 /**
11  * Stateless objects used to perform math on the various Number subclasses.
12  * Instances are required so that polymorphic calls work properly, but each
13  * subclass creates a singleton instance to minimize garbage. All methods
14  * must be thread-safe.
15  *
16  * The design goals of this class are as follows:
17  * <ol>
18  * <li>Support a 'least surprising' math model to scripting language users. This
19  * means that exact, or decimal math should be used for default calculations. This
20  * scheme assumes that by default, groovy literals with decimal points are instantiated
21  * as BigDecimal objects rather than binary floating points (Float, Double).
22  * <li>Do not force the appearance of exactness on a number that is by definition not
23  * guaranteed to be exact. In particular this means that if an operand in a NumberMath
24  * operation is a binary floating point number, ensure that the result remains a binary floating point
25  * number (i.e. never automatically promote a binary floating point number to a BigDecimal).
26  * This has the effect of preserving the expectations of binary floating point users and helps performance.
27  * <li>Provide an implementation that is as close as practical to the Java 1.5 BigDecimal math model
28  * which implements precision based floating point decimal math (ANSI X3.274-1996 and
29  * ANSI X3.274-1996/AM 1-2000 (section 7.4).
30  * </ol>
31  *
32  * @author Steve Goetze
33  */

34 public abstract class NumberMath extends Object JavaDoc {
35         
36     public static Number JavaDoc abs(Number JavaDoc number) {
37         return getMath(number).absImpl(number);
38     }
39     
40     public static Number JavaDoc add(Number JavaDoc left, Number JavaDoc right) {
41         return getMath(left, right).addImpl(left,right);
42     }
43     
44     public static Number JavaDoc subtract(Number JavaDoc left, Number JavaDoc right) {
45         return getMath(left,right).subtractImpl(left,right);
46     }
47     
48     public static Number JavaDoc multiply(Number JavaDoc left, Number JavaDoc right) {
49         return getMath(left,right).multiplyImpl(left,right);
50     }
51     
52     public static Number JavaDoc divide(Number JavaDoc left, Number JavaDoc right) {
53         return getMath(left,right).divideImpl(left,right);
54     }
55      
56     public static int compareTo(Number JavaDoc left, Number JavaDoc right) {
57         return getMath(left,right).compareToImpl(left, right);
58     }
59     
60     public static Number JavaDoc or(Number JavaDoc left, Number JavaDoc right) {
61         return getMath(left,right).orImpl(left, right);
62     }
63     
64     public static Number JavaDoc and(Number JavaDoc left, Number JavaDoc right) {
65         return getMath(left,right).andImpl(left, right);
66     }
67     
68     public static Number JavaDoc intdiv(Number JavaDoc left, Number JavaDoc right) {
69         return getMath(left,right).intdivImpl(left,right);
70     }
71
72     public static Number JavaDoc mod(Number JavaDoc left, Number JavaDoc right) {
73         return getMath(left,right).modImpl(left, right);
74     }
75
76     /**
77      * For this operation, consider the operands independently. Throw an exception if the right operand
78      * (shift distance) is not an integral type. For the left operand (shift value) also require an integral
79      * type, but do NOT promote from Integer to Long. This is consistent with Java, and makes sense for the
80      * shift operators.
81      */

82     public static Number JavaDoc leftShift(Number JavaDoc left, Number JavaDoc right) {
83         if (isFloatingPoint(right) || isBigDecimal(right)) {
84             throw new UnsupportedOperationException JavaDoc("Shift distance must be an integral type, but " + right + " (" + right.getClass().getName() + ") was supplied");
85         }
86         return getMath(left).leftShiftImpl(left,right);
87     }
88     
89     /**
90      * For this operation, consider the operands independently. Throw an exception if the right operand
91      * (shift distance) is not an integral type. For the left operand (shift value) also require an integral
92      * type, but do NOT promote from Integer to Long. This is consistent with Java, and makes sense for the
93      * shift operators.
94      */

95     public static Number JavaDoc rightShift(Number JavaDoc left, Number JavaDoc right) {
96         if (isFloatingPoint(right) || isBigDecimal(right)) {
97             throw new UnsupportedOperationException JavaDoc("Shift distance must be an integral type, but " + right + " (" + right.getClass().getName() + ") was supplied");
98         }
99         return getMath(left).rightShiftImpl(left,right);
100     }
101     
102     /**
103      * For this operation, consider the operands independently. Throw an exception if the right operand
104      * (shift distance) is not an integral type. For the left operand (shift value) also require an integral
105      * type, but do NOT promote from Integer to Long. This is consistent with Java, and makes sense for the
106      * shift operators.
107      */

108     public static Number JavaDoc rightShiftUnsigned(Number JavaDoc left, Number JavaDoc right) {
109         if (isFloatingPoint(right) || isBigDecimal(right)) {
110             throw new UnsupportedOperationException JavaDoc("Shift distance must be an integral type, but " + right + " (" + right.getClass().getName() + ") was supplied");
111         }
112         return getMath(left).rightShiftUnsignedImpl(left,right);
113     }
114     
115     public static Number JavaDoc negate(Number JavaDoc left) {
116         return getMath(left).negateImpl(left);
117     }
118     
119     public static boolean isFloatingPoint(Number JavaDoc number) {
120         return number instanceof Double JavaDoc || number instanceof Float JavaDoc;
121     }
122
123     public static boolean isInteger(Number JavaDoc number) {
124         return number instanceof Integer JavaDoc;
125     }
126
127     public static boolean isLong(Number JavaDoc number) {
128         return number instanceof Long JavaDoc;
129     }
130
131     public static boolean isBigDecimal(Number JavaDoc number) {
132         return number instanceof BigDecimal JavaDoc;
133     }
134
135     public static boolean isBigInteger(Number JavaDoc number) {
136         return number instanceof BigInteger JavaDoc;
137     }
138
139     public static BigDecimal JavaDoc toBigDecimal(Number JavaDoc n) {
140         return (n instanceof BigDecimal JavaDoc ? (BigDecimal JavaDoc) n : new BigDecimal JavaDoc(n.toString()));
141     }
142                 
143     public static BigInteger JavaDoc toBigInteger(Number JavaDoc n) {
144         return (n instanceof BigInteger JavaDoc ? (BigInteger JavaDoc) n : new BigInteger JavaDoc(n.toString()));
145     }
146                     
147     /**
148      * Determine which NumberMath instance to use, given the supplied operands. This method implements
149      * the type promotion rules discussed in the documentation. Note that by the time this method is
150      * called, any Byte, Character or Short operands will have been promoted to Integer. For reference,
151      * here is the promotion matrix:
152      * bD bI D F L I
153      * bD bD bD D D bD bD
154      * bI bD bI D D bI bI
155      * D D D D D D D
156      * F D D D D D D
157      * L bD bI D D L L
158      * I bD bI D D L I
159      *
160      * Note that for division, if either operand isFloatingPoint, the result will be floating. Otherwise,
161      * the result is BigDecimal
162      */

163     private static NumberMath getMath(Number JavaDoc left, Number JavaDoc right) {
164         if (isFloatingPoint(left) || isFloatingPoint(right)) {
165             return FloatingPointMath.instance;
166         }
167         else if (isBigDecimal(left) || isBigDecimal(right)) {
168             return BigDecimalMath.instance;
169         }
170         else if (isBigInteger(left) || isBigInteger(right)) {
171             return BigIntegerMath.instance;
172         }
173         else if (isLong(left) || isLong(right)){
174             return LongMath.instance;
175         }
176         return IntegerMath.instance;
177     }
178
179     private static NumberMath getMath(Number JavaDoc number) {
180         if (isInteger(number)) {
181             return IntegerMath.instance;
182         }
183         else if (isLong(number)) {
184             return LongMath.instance;
185         }
186         else if (isFloatingPoint(number)) {
187             return FloatingPointMath.instance;
188         }
189         else if (isBigDecimal(number)) {
190             return BigDecimalMath.instance;
191         }
192         else if (isBigInteger(number)) {
193             return BigIntegerMath.instance;
194         }
195         else {
196             throw new IllegalArgumentException JavaDoc("An unexpected Number subclass was supplied.");
197         }
198     }
199     
200     //Subclasses implement according to the type promotion hierarchy rules
201
protected abstract Number JavaDoc absImpl(Number JavaDoc number);
202     protected abstract Number JavaDoc addImpl(Number JavaDoc left, Number JavaDoc right);
203     protected abstract Number JavaDoc subtractImpl(Number JavaDoc left, Number JavaDoc right);
204     protected abstract Number JavaDoc multiplyImpl(Number JavaDoc left, Number JavaDoc right);
205     protected abstract Number JavaDoc divideImpl(Number JavaDoc left, Number JavaDoc right);
206     protected abstract int compareToImpl(Number JavaDoc left, Number JavaDoc right);
207     protected abstract Number JavaDoc negateImpl(Number JavaDoc left);
208
209
210     protected Number JavaDoc andImpl(Number JavaDoc left, Number JavaDoc right) {
211         throw createUnsupportedException("and()", left);
212     }
213
214     protected Number JavaDoc modImpl(Number JavaDoc left, Number JavaDoc right) {
215         throw createUnsupportedException("mod()", left);
216     }
217     
218     protected Number JavaDoc intdivImpl(Number JavaDoc left, Number JavaDoc right) {
219         throw createUnsupportedException("intdiv()", left);
220     }
221     
222     protected Number JavaDoc orImpl(Number JavaDoc left, Number JavaDoc right) {
223         throw createUnsupportedException("or()", left);
224     }
225     
226     protected Number JavaDoc leftShiftImpl(Number JavaDoc left, Number JavaDoc right) {
227         throw createUnsupportedException("leftShift()", left);
228     }
229
230     protected Number JavaDoc rightShiftImpl(Number JavaDoc left, Number JavaDoc right) {
231         throw createUnsupportedException("rightShift()", left);
232     }
233
234     protected Number JavaDoc rightShiftUnsignedImpl(Number JavaDoc left, Number JavaDoc right) {
235         throw createUnsupportedException("rightShiftUnsigned()", left);
236     }
237
238     protected UnsupportedOperationException JavaDoc createUnsupportedException(String JavaDoc operation, Number JavaDoc left) {
239         return new UnsupportedOperationException JavaDoc("Cannot use " + operation + " on this number type: " + left.getClass().getName() + " with value: " + left);
240     }
241 }
242
Popular Tags