|
|||||||||||||||||||
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover | |||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
NumberMath.java | 0% | 0% | 0% | 0% |
|
1 |
/*
|
|
2 |
* Created on Mar 7, 2004
|
|
3 |
*
|
|
4 |
*/
|
|
5 |
package org.codehaus.groovy.runtime;
|
|
6 |
|
|
7 |
import java.math.BigDecimal;
|
|
8 |
import java.math.BigInteger;
|
|
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 { |
|
35 |
|
|
36 | 0 |
public static Number abs(Number number) { |
37 | 0 |
return getMath(number).absImpl(number);
|
38 |
} |
|
39 |
|
|
40 | 0 |
public static Number add(Number left, Number right) { |
41 | 0 |
return getMath(left, right).addImpl(left,right);
|
42 |
} |
|
43 |
|
|
44 | 0 |
public static Number subtract(Number left, Number right) { |
45 | 0 |
return getMath(left,right).subtractImpl(left,right);
|
46 |
} |
|
47 |
|
|
48 | 0 |
public static Number multiply(Number left, Number right) { |
49 | 0 |
return getMath(left,right).multiplyImpl(left,right);
|
50 |
} |
|
51 |
|
|
52 | 0 |
public static Number divide(Number left, Number right) { |
53 | 0 |
return getMath(left,right).divideImpl(left,right);
|
54 |
} |
|
55 |
|
|
56 | 0 |
public static int compareTo(Number left, Number right) { |
57 | 0 |
return getMath(left,right).compareToImpl(left, right);
|
58 |
} |
|
59 |
|
|
60 | 0 |
public static Number or(Number left, Number right) { |
61 | 0 |
return getMath(left,right).orImpl(left, right);
|
62 |
} |
|
63 |
|
|
64 | 0 |
public static Number and(Number left, Number right) { |
65 | 0 |
return getMath(left,right).andImpl(left, right);
|
66 |
} |
|
67 |
|
|
68 | 0 |
public static Number intdiv(Number left, Number right) { |
69 | 0 |
return getMath(left,right).intdivImpl(left,right);
|
70 |
} |
|
71 |
|
|
72 | 0 |
public static Number mod(Number left, Number right) { |
73 | 0 |
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 | 0 |
public static Number leftShift(Number left, Number right) { |
83 | 0 |
if (isFloatingPoint(right) || isBigDecimal(right)) {
|
84 | 0 |
throw new UnsupportedOperationException("Shift distance must be an integral type, but " + right + " (" + right.getClass().getName() + ") was supplied"); |
85 |
} |
|
86 | 0 |
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 | 0 |
public static Number rightShift(Number left, Number right) { |
96 | 0 |
if (isFloatingPoint(right) || isBigDecimal(right)) {
|
97 | 0 |
throw new UnsupportedOperationException("Shift distance must be an integral type, but " + right + " (" + right.getClass().getName() + ") was supplied"); |
98 |
} |
|
99 | 0 |
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 | 0 |
public static Number rightShiftUnsigned(Number left, Number right) { |
109 | 0 |
if (isFloatingPoint(right) || isBigDecimal(right)) {
|
110 | 0 |
throw new UnsupportedOperationException("Shift distance must be an integral type, but " + right + " (" + right.getClass().getName() + ") was supplied"); |
111 |
} |
|
112 | 0 |
return getMath(left).rightShiftUnsignedImpl(left,right);
|
113 |
} |
|
114 |
|
|
115 | 0 |
public static Number negate(Number left) { |
116 | 0 |
return getMath(left).negateImpl(left);
|
117 |
} |
|
118 |
|
|
119 | 0 |
public static boolean isFloatingPoint(Number number) { |
120 | 0 |
return number instanceof Double || number instanceof Float; |
121 |
} |
|
122 |
|
|
123 | 0 |
public static boolean isInteger(Number number) { |
124 | 0 |
return number instanceof Integer; |
125 |
} |
|
126 |
|
|
127 | 0 |
public static boolean isLong(Number number) { |
128 | 0 |
return number instanceof Long; |
129 |
} |
|
130 |
|
|
131 | 0 |
public static boolean isBigDecimal(Number number) { |
132 | 0 |
return number instanceof BigDecimal; |
133 |
} |
|
134 |
|
|
135 | 0 |
public static boolean isBigInteger(Number number) { |
136 | 0 |
return number instanceof BigInteger; |
137 |
} |
|
138 |
|
|
139 | 0 |
public static BigDecimal toBigDecimal(Number n) { |
140 | 0 |
return (n instanceof BigDecimal ? (BigDecimal) n : new BigDecimal(n.toString())); |
141 |
} |
|
142 |
|
|
143 | 0 |
public static BigInteger toBigInteger(Number n) { |
144 | 0 |
return (n instanceof BigInteger ? (BigInteger) n : new BigInteger(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 | 0 |
private static NumberMath getMath(Number left, Number right) { |
164 | 0 |
if (isFloatingPoint(left) || isFloatingPoint(right)) {
|
165 | 0 |
return FloatingPointMath.instance;
|
166 |
} |
|
167 | 0 |
else if (isBigDecimal(left) || isBigDecimal(right)) { |
168 | 0 |
return BigDecimalMath.instance;
|
169 |
} |
|
170 | 0 |
else if (isBigInteger(left) || isBigInteger(right)) { |
171 | 0 |
return BigIntegerMath.instance;
|
172 |
} |
|
173 | 0 |
else if (isLong(left) || isLong(right)){ |
174 | 0 |
return LongMath.instance;
|
175 |
} |
|
176 | 0 |
return IntegerMath.instance;
|
177 |
} |
|
178 |
|
|
179 | 0 |
private static NumberMath getMath(Number number) { |
180 | 0 |
if (isInteger(number)) {
|
181 | 0 |
return IntegerMath.instance;
|
182 |
} |
|
183 | 0 |
else if (isLong(number)) { |
184 | 0 |
return LongMath.instance;
|
185 |
} |
|
186 | 0 |
else if (isFloatingPoint(number)) { |
187 | 0 |
return FloatingPointMath.instance;
|
188 |
} |
|
189 | 0 |
else if (isBigDecimal(number)) { |
190 | 0 |
return BigDecimalMath.instance;
|
191 |
} |
|
192 | 0 |
else if (isBigInteger(number)) { |
193 | 0 |
return BigIntegerMath.instance;
|
194 |
} |
|
195 |
else {
|
|
196 | 0 |
throw new IllegalArgumentException("An unexpected Number subclass was supplied."); |
197 |
} |
|
198 |
} |
|
199 |
|
|
200 |
//Subclasses implement according to the type promotion hierarchy rules
|
|
201 |
protected abstract Number absImpl(Number number);
|
|
202 |
protected abstract Number addImpl(Number left, Number right);
|
|
203 |
protected abstract Number subtractImpl(Number left, Number right);
|
|
204 |
protected abstract Number multiplyImpl(Number left, Number right);
|
|
205 |
protected abstract Number divideImpl(Number left, Number right);
|
|
206 |
protected abstract int compareToImpl(Number left, Number right); |
|
207 |
protected abstract Number negateImpl(Number left);
|
|
208 |
|
|
209 |
|
|
210 | 0 |
protected Number andImpl(Number left, Number right) {
|
211 | 0 |
throw createUnsupportedException("and()", left); |
212 |
} |
|
213 |
|
|
214 | 0 |
protected Number modImpl(Number left, Number right) {
|
215 | 0 |
throw createUnsupportedException("mod()", left); |
216 |
} |
|
217 |
|
|
218 | 0 |
protected Number intdivImpl(Number left, Number right) {
|
219 | 0 |
throw createUnsupportedException("intdiv()", left); |
220 |
} |
|
221 |
|
|
222 | 0 |
protected Number orImpl(Number left, Number right) {
|
223 | 0 |
throw createUnsupportedException("or()", left); |
224 |
} |
|
225 |
|
|
226 | 0 |
protected Number leftShiftImpl(Number left, Number right) {
|
227 | 0 |
throw createUnsupportedException("leftShift()", left); |
228 |
} |
|
229 |
|
|
230 | 0 |
protected Number rightShiftImpl(Number left, Number right) {
|
231 | 0 |
throw createUnsupportedException("rightShift()", left); |
232 |
} |
|
233 |
|
|
234 | 0 |
protected Number rightShiftUnsignedImpl(Number left, Number right) {
|
235 | 0 |
throw createUnsupportedException("rightShiftUnsigned()", left); |
236 |
} |
|
237 |
|
|
238 | 0 |
protected UnsupportedOperationException createUnsupportedException(String operation, Number left) {
|
239 | 0 |
return new UnsupportedOperationException("Cannot use " + operation + " on this number type: " + left.getClass().getName() + " with value: " + left); |
240 |
} |
|
241 |
} |
|
242 |
|
|