Java tutorial
/** * Copyright 2004, 2005, 2006 Odysseus Software GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.math.BigDecimal; import java.math.BigInteger; /** * Number utilities. * * Allows to convert between different <code>java.lang.Number</code> * implementations with a minimum of lost information regarding the * value of the represented number. Additionally, a few number tests * are implemented and exact comparisons of arbitrary numbers may be * performed. * * NOTE: Though some of the methods may give more or less useful results * for custom number implementations, they are intended to work only * with the predefined types (i.e., <code>Byte, Short, Integer, Long, * Float, Double, BigInteger, BigDecimal</code>). * * @author Oliver Stuhr */ public class NumberUtils { /** * Answers <code>true</code> iff the given number is an instance of * <code>java.math.BigDecimal</code> or <code>java.math.BigInteger</code>. * * @param number * @return boolean */ public static boolean isBig(Number number) { return number instanceof BigDecimal || number instanceof BigInteger; } /** * Answers <code>true</code> iff the given number is an instance of * <code>Byte</code>, <code>Short</code>, <code>Integer</code> or <code>Long</code>. * * @param number * @return boolean */ public static boolean isLongCompatible(Number number) { return number instanceof Byte || number instanceof Short || number instanceof Integer || number instanceof Long; } /** * Answers <code>true</code> iff the given number is an instance of * <code>Float</code> or <code>Double</code>. * * @param number * @return boolean */ public static boolean isDoubleCompatible(Number number) { return number instanceof Float || number instanceof Double; } /** * Answers <code>true</code> iff the given number is infinite (i.e., is * a <code>Float</code> or <code>Double</code> containing one of the * predefined constant values representing positive or negative infinity). * * @param number * @return boolean */ public static boolean isInfinite(Number number) { if (number instanceof Double && ((Double) number).isInfinite()) return true; if (number instanceof Float && ((Float) number).isInfinite()) return true; return false; } /** * Answers <code>true</code> iff the given number is 'not a number' * (i.e., is a <code>Float</code> or <code>Double</code> containing * one of the predefined constant values representing <code>NaN</code>). * * @param number * @return boolean */ public static boolean isNaN(Number number) { if (number instanceof Double && ((Double) number).isNaN()) return true; if (number instanceof Float && ((Float) number).isNaN()) return true; return false; } /** * Answers the signum function of the given number * (i.e., <code>-1</code> if it is negative, <code>0</code> * if it is zero and <code>1</code> if it is positive). * * @param number * @return int * @throws ArithmeticException The given number is <code>null</code> or 'not a number'. */ public static int signum(Number number) throws ArithmeticException { if (number == null || isNaN(number)) throw new ArithmeticException("Argument must not be null or NaN."); if (isLongCompatible(number)) { long value = number.longValue(); return value < 0 ? -1 : value == 0 ? 0 : 1; } else if (number instanceof BigInteger) return ((BigInteger) number).signum(); else if (number instanceof BigDecimal) return ((BigDecimal) number).signum(); else { // => isDoubleCompatible(number) or unknown Number type double value = number.doubleValue(); return value < 0 ? -1 : value == 0 ? 0 : 1; } } /** * Converts the given number to a <code>Byte</code> (by using <code>byteValue()</code>). * * @param number * @return java.lang.Byte * @throws IllegalArgumentException The given number is 'not a number' or infinite. */ public static Byte toByte(Number number) throws IllegalArgumentException { if (number == null || number instanceof Byte) return (Byte) number; if (isNaN(number) || isInfinite(number)) throw new IllegalArgumentException("Argument must not be NaN or infinite."); return new Byte(number.byteValue()); } /** * Converts the given number to a <code>Short</code> (by using <code>shortValue()</code>). * * @param number * @return java.lang.Short * @throws IllegalArgumentException The given number is 'not a number' or infinite. */ public static Short toShort(Number number) throws IllegalArgumentException { if (number == null || number instanceof Short) return (Short) number; if (isNaN(number) || isInfinite(number)) throw new IllegalArgumentException("Argument must not be NaN or infinite."); return new Short(number.shortValue()); } /** * Converts the given number to a <code>Integer</code> (by using <code>intValue()</code>). * * @param number * @return java.lang.Integer * @throws IllegalArgumentException The given number is 'not a number' or infinite. */ public static Integer toInteger(Number number) throws IllegalArgumentException { if (number == null || number instanceof Integer) return (Integer) number; if (isNaN(number) || isInfinite(number)) throw new IllegalArgumentException("Argument must not be NaN or infinite."); return new Integer(number.intValue()); } /** * Converts the given number to a <code>Long</code> (by using <code>longValue()</code>). * * @param number * @return java.lang.Long * @throws IllegalArgumentException The given number is 'not a number' or infinite. */ public static Long toLong(Number number) throws IllegalArgumentException { if (number == null || number instanceof Long) return (Long) number; if (isNaN(number) || isInfinite(number)) throw new IllegalArgumentException("Argument must not be NaN or infinite."); return new Long(number.longValue()); } /** * Converts the given number to a <code>Float</code> (by using <code>floatValue()</code>). * * @param number * @return java.lang.Float */ public static Float toFloat(Number number) { return number == null || number instanceof Float ? (Float) number : new Float(number.floatValue()); } /** * Converts the given number to a <code>Double</code> (by using <code>doubleValue()</code>). * * @param number * @return java.lang.Double */ public static Double toDouble(Number number) { return number == null || number instanceof Double ? (Double) number : new Double(number.doubleValue()); } /** * Converts the given number to a <code>java.math.BigInteger</code>. * * @param number * @return java.math.BigInteger * @throws IllegalArgumentException The given number is 'not a number' or infinite. */ public static BigInteger toBigInteger(Number number) throws IllegalArgumentException { if (number == null || number instanceof BigInteger) return (BigInteger) number; if (number instanceof BigDecimal) return ((BigDecimal) number).toBigInteger(); if (isDoubleCompatible(number)) { if (isNaN(number) || isInfinite(number)) throw new IllegalArgumentException("Argument must not be NaN or infinite."); return new BigDecimal(number.toString()).toBigInteger(); } // => isLongCompatible(number) or unknown Number type return BigInteger.valueOf(number.longValue()); } /** * Converts the given number to a <code>java.math.BigDecimal</code>. * * @param number * @return java.math.BigDecimal * @throws IllegalArgumentException The given number is 'not a number' or infinite. */ public static BigDecimal toBigDecimal(Number number) throws IllegalArgumentException { if (number == null || number instanceof BigDecimal) return (BigDecimal) number; if (number instanceof BigInteger) return new BigDecimal((BigInteger) number); if (isDoubleCompatible(number)) { if (isNaN(number) || isInfinite(number)) throw new IllegalArgumentException("Argument must not be NaN or infinite."); return new BigDecimal(number.toString()); } if (isLongCompatible(number)) return BigDecimal.valueOf(number.longValue()); // => unknown Number type return new BigDecimal(String.valueOf(number.doubleValue())); } /** * Compares the first number to the second one numerically and * returns an integer depending on the comparison result: * a negative value if the first number is the smaller one, * a zero value if they are equal, and * a positive value if the first number is the larger one. * * The main strategy goes like follows: * 1. If one of the arguments is <code>null</code> or 'not a number', * throw an exception. * 2. If both values are 'long compatible', compare their <code>longValue()</code> * using the usual comparison operators for primitive types (<, ==, >). * 3. If both values are 'double compatible', compare their <code>doubleValue()</code> * using the usual comparison operators for primitive types (<, ==, >). * 4. If one of the values is infinite (and the other is finite), * determine the result depending on the sign of the infinite value. * 5. Otherwise convert both values to <code>java.math.BigDecimal</code> and * return the result of the <code>BigDecimal.compareTo(BigDecimal)</code> method. * * As a consequence, the method is not suitable to implement a * <code>java.util.Comparator</code> for numbers. To achieve this, * one had to accept 'not a number' arguments and place them somewhere * in the row of numbers (probably at the upper end, i.e. larger than * positive infinity, as <code>Double.compare(double, double)</code> * does it). * So the behavior of this method is like that of the comparison * operator for primitive types and not like that of the related * <code>compareTo(...)</code> methods. Besides the handling of * 'not a number' values this makes a difference, when comparing * the float or double values <code>-0.0</code> and <code>0.0</code>: * again, like the operators, we consider them as equal (whereas * according to <code>Double.compareTo(...)</code> <code>-0.0</code> * is less than <code>0.0</code>). * * @param first * @param second * @return int * @throws ArithmeticException One or both of the given numbers is <code>null</code> or 'not a number'. */ public static int compare(Number first, Number second) throws ArithmeticException { if (first == null || second == null || isNaN(first) || isNaN(second)) throw new ArithmeticException("Arguments must not be null or NaN."); int result = -2; if (isLongCompatible(first) && isLongCompatible(second)) { long v1 = first.longValue(), v2 = second.longValue(); result = v1 < v2 ? -1 : v1 == v2 ? 0 : v1 > v2 ? 1 : 2; } else if (isDoubleCompatible(first) && isDoubleCompatible(second)) { double v1 = first.doubleValue(), v2 = second.doubleValue(); result = v1 < v2 ? -1 : v1 == v2 ? 0 : v1 > v2 ? 1 : 2; } if (result == 2) // should not happen throw new ArithmeticException("Arguments " + first + " and " + second + " are not comparable."); if (result > -2) return result; if (isInfinite(first)) // => second is finite return first.doubleValue() == Double.NEGATIVE_INFINITY ? -1 : 1; if (isInfinite(second)) // => first is finite return second.doubleValue() == Double.POSITIVE_INFINITY ? -1 : 1; return toBigDecimal(first).compareTo(toBigDecimal(second)); } }