org.jopendocument.util.NumberUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jOpenDocument Show documentation
Show all versions of jOpenDocument Show documentation
jOpenDocument is a free library for developers looking to use
Open Document files without OpenOffice.org.
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008-2013 jOpenDocument, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU
* General Public License Version 3 only ("GPL").
* You may not use this file except in compliance with the License.
* You can obtain a copy of the License at http://www.gnu.org/licenses/gpl-3.0.html
* See the License for the specific language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*
*/
package org.jopendocument.util;
import org.jopendocument.util.convertor.NumberConvertor;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class NumberUtils {
/**
* Test class and numerical equality. E.g. {@link BigDecimal#equals(Object)} also tests the
* scale.
*
* @param type of number.
* @param n1 first number, can be null
.
* @param n2 second number, can be null
.
* @return true
if n1
and n2
have the same class and are
* numerically equal.
* @see #areNumericallyEqual(Number, Number)
*/
static public final boolean areEqual(final N n1, final N n2) {
if (n1 == null && n2 == null)
return true;
if (n1 == null || n2 == null)
return false;
final Class n1Class = n1.getClass();
if (n1Class != n2.getClass())
return false;
// Atomic* don't implement equals()
if (n1Class == AtomicInteger.class || n1Class == AtomicLong.class)
return n1.longValue() == n2.longValue();
else if (n1Class == BigDecimal.class)
return ((BigDecimal) n1).compareTo((BigDecimal) n2) == 0;
else
return n1.equals(n2);
}
/**
* Test numerical equality (but ignore class).
*
* @param n1 first number, can be null
.
* @param n2 second number, can be null
.
* @return true
if n1
and n2
are numerically equal.
* @see #compare(Number, Number)
*/
static public final boolean areNumericallyEqual(final Number n1, final Number n2) {
if (n1 == null && n2 == null)
return true;
if (n1 == null || n2 == null)
return false;
return compare(n1, n2) == 0;
}
/**
* Compare two arbitrary numbers.
*
* @param n1 first number, not null
.
* @param n2 second number, not null
.
* @return a negative integer, zero, or a positive integer as n1 is less than, equal to, or
* greater than n2.
* @see Comparable#compareTo(Object)
*/
static public final int compare(final Number n1, final Number n2) {
Class biggerClass = getWiderClass(n1, n2);
// Atomic* aren't Comparable
if (biggerClass == AtomicInteger.class)
biggerClass = Integer.class;
else if (biggerClass == AtomicLong.class)
biggerClass = Long.class;
return compare(n1, n2, biggerClass);
}
static private final int compare(final Number n1, final Number n2, Class clazz) {
final N n1Converted = NumberConvertor.convertExact(n1, clazz);
final N n2Converted = NumberConvertor.convertExact(n2, clazz);
@SuppressWarnings("unchecked")
final Comparable comparable = (Comparable) n1Converted;
return comparable.compareTo(n2Converted);
}
/**
* Return a class wide enough for both numbers. E.g. for Integer and Short, Integer ; for
* BigInteger and Float, BigDecimal.
*
* @param n1 first number, not null
.
* @param n2 second number, not null
.
* @return a class wide enough for both numbers.
* @see NumberConvertor#convertExact(Number, Class)
*/
static public final Class getWiderClass(final Number n1, final Number n2) {
final Class n1Class = n1.getClass();
final Class n2Class = n2.getClass();
if (n1Class == n2Class)
return n1Class;
if (n1Class == BigDecimal.class || n2Class == BigDecimal.class)
return BigDecimal.class;
final boolean n1isFloat = n1Class == Float.class || n1Class == Double.class;
final boolean n2isFloat = n2Class == Float.class || n2Class == Double.class;
if (n1isFloat && n2isFloat) {
// since classes are different, at least one is Double
return Double.class;
} else if (n1isFloat || n2isFloat) {
// the only class (except the already handled BigDecimal) that can overflow in a Double
// is BigInteger
if (n1Class == BigInteger.class || n2Class == BigInteger.class)
return BigDecimal.class;
else
return Double.class;
}
// integers or BigInteger
if (n1Class == BigInteger.class || n2Class == BigInteger.class)
return BigInteger.class;
else if (n1Class == Long.class || n2Class == Long.class || n1Class == AtomicLong.class || n2Class == AtomicLong.class)
return Long.class;
else if (n1Class == Integer.class || n2Class == Integer.class || n1Class == AtomicInteger.class || n2Class == AtomicInteger.class)
return Integer.class;
else if (n1Class == Short.class || n2Class == Short.class)
return Short.class;
else if (n1Class == Byte.class || n2Class == Byte.class)
return Byte.class;
else
throw new IllegalStateException("Unknown classes " + n1Class + " / " + n2Class);
}
/**
* Whether n
has a non-zero fractional part.
*
* @param n a number.
* @return true
if there is a non-zero fractional part, e.g. true
for
* 1.3d and false
for new BigDecimal("1.00")
.
*/
static public final boolean hasFractionalPart(Number n) {
if (n instanceof Integer || n instanceof Long || n instanceof Short || n instanceof Byte || n instanceof BigInteger || n instanceof AtomicLong || n instanceof AtomicInteger)
return false;
final BigDecimal bd;
if (n instanceof BigDecimal)
bd = (BigDecimal) n;
else if (n instanceof Double || n instanceof Float)
bd = new BigDecimal(n.doubleValue());
else
bd = new BigDecimal(n.toString());
return DecimalUtils.decimalDigits(bd) > 0;
}
static final int MAX_LONG_LENGTH = String.valueOf(Long.MAX_VALUE).length();
static public final int intDigits(final long l) {
final long x = Math.abs(l);
long p = 10;
int i = 1;
while (x >= p && i < MAX_LONG_LENGTH) {
p = 10 * p;
i++;
}
return i;
}
/**
* The number of digits of the integer part in decimal representation.
*
* @param n a number, e.g. 123.45.
* @return the number of digits of the integer part, e.g. 3.
*/
static public final int intDigits(Number n) {
if (n instanceof Integer || n instanceof Long || n instanceof Short || n instanceof Byte || n instanceof AtomicLong || n instanceof AtomicInteger)
return intDigits(n.longValue());
final BigDecimal bd;
if (n instanceof BigDecimal)
bd = (BigDecimal) n;
else if (n instanceof BigInteger)
bd = new BigDecimal((BigInteger) n);
else if (n instanceof Double || n instanceof Float)
bd = new BigDecimal(n.doubleValue());
else
bd = new BigDecimal(n.toString());
return DecimalUtils.intDigits(bd);
}
/**
* High precision divide.
*
* @param n the dividend.
* @param d the divisor.
* @return n / d
.
* @see DecimalUtils#HIGH_PRECISION
*/
static public Number divide(Number n, double d) {
if (d == 1)
return n;
if (n instanceof BigDecimal) {
return ((BigDecimal) n).divide(new BigDecimal(d), DecimalUtils.HIGH_PRECISION);
} else if (n instanceof BigInteger) {
return new BigDecimal((BigInteger) n).divide(new BigDecimal(d), DecimalUtils.HIGH_PRECISION);
} else {
return n.doubleValue() / d;
}
}
}