blob: 978b576db2b5aa38f89f003b0fe4f1f2e5ae68dc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2018 IBM Corporation, Zeligsoft Inc., and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Andreas Werner - Initial API and implementation
* Achim Demelt - Bug 245897
*******************************************************************************/
package org.eclipse.ocl.internal.evaluation;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* Utility for converting numeric values to double or integer precision as needed.
*
* @since 1.2
*/
public class NumberUtil {
/**
* <p>
* Tests whether a given number can be safely coerced to <tt>Double</tt> or
* <tt>Integer</tt> without changing the value of the number. Safe means
* that coercing a number to <tt>Double</tt> or <tt>Integer</tt> and then
* coercing it back to the original type will result in the same value (no
* loss of precision). This is trivial for types, which have a smaller
* domain then <tt>Integer</tt> or <tt>Double</tt>, but for
* example a <tt>Long</tt> number may not be safely coerced to
* <tt>Integer</tt>.
* </p><p>
* If the coercion is safe, the number will be returned as either
* <tt>Double</tt> or <tt>Integer</tt>, as appropriate to the original
* precision. Otherwise the original number is returned.
* </p>
*
* @param number a number to coerce to <tt>Integer</tt> or <tt>Double</tt>
* @return the coerced number, or the original number, if coercion was not safe
*/
public static Number coerceNumber(Number number) {
Number result;
if ((number instanceof Integer) || (number instanceof Double)) {
result = number;
} else if ((number instanceof Byte) || (number instanceof Short)) {
result = number.intValue();
} else if (number instanceof Long) {
if (canConform((Long) number)) {
result = number.intValue();
} else {
result = number;
}
} else if (number instanceof BigInteger) {
if (isInteger((BigInteger) number)) {
result = number.intValue();
} else if (isLong((BigInteger) number)) {
result = number.longValue();
} else {
// do nothing, NFE will occur
result = number;
}
} else if (number instanceof Float) {
result = number.doubleValue();
} else if (number instanceof BigDecimal) {
if (isDouble((BigDecimal) number)) {
result = number.doubleValue();
} else {
// do nothing, NFE will occur
result = number;
}
} else {
// some odd-ball number we've never heard of. NFE will occur
result = number;
}
return result;
}
private static boolean canConform(Long number) {
long l = number;
long i = number.intValue();
return l == i;
}
private static boolean isInteger(BigInteger number) {
int i = number.intValue();
BigInteger b = new BigInteger(String.valueOf(i));
return number.equals(b);
}
private static boolean isLong(BigInteger number) {
long i = number.longValue();
BigInteger b = new BigInteger(String.valueOf(i));
return number.equals(b);
}
private static boolean isDouble(BigDecimal number) {
double doubleValue = number.doubleValue();
return (doubleValue != Double.NEGATIVE_INFINITY)
&& (doubleValue != Double.POSITIVE_INFINITY);
}
/**
* <p>
* Coerces the given number to a common or greater precision than <tt>referenceNumber</tt>,
* <tt>BigDecimal</tt> is greater than <tt>Double</tt> or
* <tt>BigInteger</tt> which is greater than <tt>Long</tt> precision.
* </p>
*
* @param number a number to coerce to a common precision
* @param referenceNumber another number to share the common precision
* @return the coerced number, or the original number, in case of overflow
*/
public static Number commonPrecisionNumber(Number number, Number referenceNumber) {
if ((number instanceof BigDecimal) || (referenceNumber instanceof BigDecimal)) {
if (number instanceof BigDecimal) {
return number;
} else if (number instanceof BigInteger) {
return new BigDecimal((BigInteger)number);
} else if (number instanceof Integer || number instanceof Long || number instanceof Short || number instanceof Byte) {
return new BigDecimal(number.longValue());
} else {
return BigDecimal.valueOf(number.doubleValue());
}
} else if ((number instanceof BigInteger) && (referenceNumber instanceof Double)) {
return new BigDecimal((BigInteger)number);
} else if ((number instanceof Double) && (referenceNumber instanceof BigInteger)) {
return BigDecimal.valueOf(number.doubleValue());
} else if ((number instanceof Double) || (referenceNumber instanceof Double)
|| (number instanceof Float) || (referenceNumber instanceof Float)) {
if (number instanceof Double) {
return number;
} else {
return Double.valueOf(number.doubleValue());
}
} else if ((number instanceof BigInteger) || (referenceNumber instanceof BigInteger)) {
if (number instanceof BigInteger) {
return number;
} else {
return BigInteger.valueOf(number.longValue());
}
} else {
if (number instanceof Long) {
return number;
} else {
return Long.valueOf(number.longValue());
}
}
}
/**
* <p>
* Coerces the given number to <tt>Double</tt> or <tt>Long</tt> precision,
* if possible. Note that this is only impossible for <tt>BigDecimal</tt>
* or <tt>BigInteger</tt> values, respectively, that are out of range of
* their primitive counterparts.
* </p>
*
* @param number a number to coerce to <tt>Long</tt> or <tt>Double</tt>
* @return the coerced number, or the original number, in case of overflow
*/
public static Number higherPrecisionNumber(Number number) {
Number result;
if ((number instanceof Integer) || (number instanceof Byte) ||
(number instanceof Short)) {
result = number.longValue();
} else if (number instanceof Long) {
result = number;
} else if (number instanceof BigInteger) {
if (isLong((BigInteger) number)) {
result = number.longValue();
} else {
// do nothing, NFE will occur
result = number;
}
} else if (number instanceof Float) {
result = number.doubleValue();
} else if (number instanceof BigDecimal) {
if (isDouble((BigDecimal) number)) {
result = number.doubleValue();
} else {
// do nothing, NFE will occur
result = number;
}
} else {
// some odd-ball number we've never heard of. NFE will occur
result = number;
}
return result;
}
}