001 /* 002 * Created on Mar 7, 2004 003 * 004 */ 005 package org.codehaus.groovy.runtime; 006 007 import java.math.BigDecimal; 008 import java.math.BigInteger; 009 010 /** 011 * Stateless objects used to perform math on the various Number subclasses. 012 * Instances are required so that polymorphic calls work properly, but each 013 * subclass creates a singleton instance to minimize garbage. All methods 014 * must be thread-safe. 015 * 016 * The design goals of this class are as follows: 017 * <ol> 018 * <li>Support a 'least surprising' math model to scripting language users. This 019 * means that exact, or decimal math should be used for default calculations. This 020 * scheme assumes that by default, groovy literals with decimal points are instantiated 021 * as BigDecimal objects rather than binary floating points (Float, Double). 022 * <li>Do not force the appearance of exactness on a number that is by definition not 023 * guaranteed to be exact. In particular this means that if an operand in a NumberMath 024 * operation is a binary floating point number, ensure that the result remains a binary floating point 025 * number (i.e. never automatically promote a binary floating point number to a BigDecimal). 026 * This has the effect of preserving the expectations of binary floating point users and helps performance. 027 * <li>Provide an implementation that is as close as practical to the Java 1.5 BigDecimal math model 028 * which implements precision based floating point decimal math (ANSI X3.274-1996 and 029 * ANSI X3.274-1996/AM 1-2000 (section 7.4). 030 * </ol> 031 * 032 * @author Steve Goetze 033 */ 034 public abstract class NumberMath extends Object { 035 036 public static Number abs(Number number) { 037 return getMath(number).absImpl(number); 038 } 039 040 public static Number add(Number left, Number right) { 041 return getMath(left, right).addImpl(left,right); 042 } 043 044 public static Number subtract(Number left, Number right) { 045 return getMath(left,right).subtractImpl(left,right); 046 } 047 048 public static Number multiply(Number left, Number right) { 049 return getMath(left,right).multiplyImpl(left,right); 050 } 051 052 public static Number divide(Number left, Number right) { 053 return getMath(left,right).divideImpl(left,right); 054 } 055 056 public static int compareTo(Number left, Number right) { 057 return getMath(left,right).compareToImpl(left, right); 058 } 059 060 public static Number or(Number left, Number right) { 061 return getMath(left,right).orImpl(left, right); 062 } 063 064 public static Number and(Number left, Number right) { 065 return getMath(left,right).andImpl(left, right); 066 } 067 068 public static Number xor(Number left, Number right) { 069 return getMath(left,right).xorImpl(left, right); 070 } 071 072 public static Number intdiv(Number left, Number right) { 073 return getMath(left,right).intdivImpl(left,right); 074 } 075 076 public static Number mod(Number left, Number right) { 077 return getMath(left,right).modImpl(left, right); 078 } 079 080 /** 081 * For this operation, consider the operands independently. Throw an exception if the right operand 082 * (shift distance) is not an integral type. For the left operand (shift value) also require an integral 083 * type, but do NOT promote from Integer to Long. This is consistent with Java, and makes sense for the 084 * shift operators. 085 */ 086 public static Number leftShift(Number left, Number right) { 087 if (isFloatingPoint(right) || isBigDecimal(right)) { 088 throw new UnsupportedOperationException("Shift distance must be an integral type, but " + right + " (" + right.getClass().getName() + ") was supplied"); 089 } 090 return getMath(left).leftShiftImpl(left,right); 091 } 092 093 /** 094 * For this operation, consider the operands independently. Throw an exception if the right operand 095 * (shift distance) is not an integral type. For the left operand (shift value) also require an integral 096 * type, but do NOT promote from Integer to Long. This is consistent with Java, and makes sense for the 097 * shift operators. 098 */ 099 public static Number rightShift(Number left, Number right) { 100 if (isFloatingPoint(right) || isBigDecimal(right)) { 101 throw new UnsupportedOperationException("Shift distance must be an integral type, but " + right + " (" + right.getClass().getName() + ") was supplied"); 102 } 103 return getMath(left).rightShiftImpl(left,right); 104 } 105 106 /** 107 * For this operation, consider the operands independently. Throw an exception if the right operand 108 * (shift distance) is not an integral type. For the left operand (shift value) also require an integral 109 * type, but do NOT promote from Integer to Long. This is consistent with Java, and makes sense for the 110 * shift operators. 111 */ 112 public static Number rightShiftUnsigned(Number left, Number right) { 113 if (isFloatingPoint(right) || isBigDecimal(right)) { 114 throw new UnsupportedOperationException("Shift distance must be an integral type, but " + right + " (" + right.getClass().getName() + ") was supplied"); 115 } 116 return getMath(left).rightShiftUnsignedImpl(left,right); 117 } 118 119 public static Number negate(Number left) { 120 return getMath(left).negateImpl(left); 121 } 122 123 public static boolean isFloatingPoint(Number number) { 124 return number instanceof Double || number instanceof Float; 125 } 126 127 public static boolean isInteger(Number number) { 128 return number instanceof Integer; 129 } 130 131 public static boolean isLong(Number number) { 132 return number instanceof Long; 133 } 134 135 public static boolean isBigDecimal(Number number) { 136 return number instanceof BigDecimal; 137 } 138 139 public static boolean isBigInteger(Number number) { 140 return number instanceof BigInteger; 141 } 142 143 public static BigDecimal toBigDecimal(Number n) { 144 return (n instanceof BigDecimal ? (BigDecimal) n : new BigDecimal(n.toString())); 145 } 146 147 public static BigInteger toBigInteger(Number n) { 148 return (n instanceof BigInteger ? (BigInteger) n : new BigInteger(n.toString())); 149 } 150 151 /** 152 * Determine which NumberMath instance to use, given the supplied operands. This method implements 153 * the type promotion rules discussed in the documentation. Note that by the time this method is 154 * called, any Byte, Character or Short operands will have been promoted to Integer. For reference, 155 * here is the promotion matrix: 156 * bD bI D F L I 157 * bD bD bD D D bD bD 158 * bI bD bI D D bI bI 159 * D D D D D D D 160 * F D D D D D D 161 * L bD bI D D L L 162 * I bD bI D D L I 163 * 164 * Note that for division, if either operand isFloatingPoint, the result will be floating. Otherwise, 165 * the result is BigDecimal 166 */ 167 private static NumberMath getMath(Number left, Number right) { 168 if (isFloatingPoint(left) || isFloatingPoint(right)) { 169 return FloatingPointMath.instance; 170 } 171 else if (isBigDecimal(left) || isBigDecimal(right)) { 172 return BigDecimalMath.instance; 173 } 174 else if (isBigInteger(left) || isBigInteger(right)) { 175 return BigIntegerMath.instance; 176 } 177 else if (isLong(left) || isLong(right)){ 178 return LongMath.instance; 179 } 180 return IntegerMath.instance; 181 } 182 183 private static NumberMath getMath(Number number) { 184 if (isInteger(number)) { 185 return IntegerMath.instance; 186 } 187 else if (isLong(number)) { 188 return LongMath.instance; 189 } 190 else if (isFloatingPoint(number)) { 191 return FloatingPointMath.instance; 192 } 193 else if (isBigDecimal(number)) { 194 return BigDecimalMath.instance; 195 } 196 else if (isBigInteger(number)) { 197 return BigIntegerMath.instance; 198 } 199 else { 200 throw new IllegalArgumentException("An unexpected Number subclass was supplied."); 201 } 202 } 203 204 //Subclasses implement according to the type promotion hierarchy rules 205 protected abstract Number absImpl(Number number); 206 protected abstract Number addImpl(Number left, Number right); 207 protected abstract Number subtractImpl(Number left, Number right); 208 protected abstract Number multiplyImpl(Number left, Number right); 209 protected abstract Number divideImpl(Number left, Number right); 210 protected abstract int compareToImpl(Number left, Number right); 211 protected abstract Number negateImpl(Number left); 212 213 214 protected Number orImpl(Number left, Number right) { 215 throw createUnsupportedException("or()", left); 216 } 217 218 protected Number andImpl(Number left, Number right) { 219 throw createUnsupportedException("and()", left); 220 } 221 222 protected Number xorImpl(Number left, Number right) { 223 throw createUnsupportedException("xor()", left); 224 } 225 226 protected Number modImpl(Number left, Number right) { 227 throw createUnsupportedException("mod()", left); 228 } 229 230 protected Number intdivImpl(Number left, Number right) { 231 throw createUnsupportedException("intdiv()", left); 232 } 233 234 protected Number leftShiftImpl(Number left, Number right) { 235 throw createUnsupportedException("leftShift()", left); 236 } 237 238 protected Number rightShiftImpl(Number left, Number right) { 239 throw createUnsupportedException("rightShift()", left); 240 } 241 242 protected Number rightShiftUnsignedImpl(Number left, Number right) { 243 throw createUnsupportedException("rightShiftUnsigned()", left); 244 } 245 246 protected UnsupportedOperationException createUnsupportedException(String operation, Number left) { 247 return new UnsupportedOperationException("Cannot use " + operation + " on this number type: " + left.getClass().getName() + " with value: " + left); 248 } 249 }