
com.alibaba.qlexpress4.runtime.operator.number.NumberMath Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package com.alibaba.qlexpress4.runtime.operator.number;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* Stateless objects used to perform math on the various Number subclasses.
* Instances are required so that polymorphic calls work properly, but each
* subclass creates a singleton instance to minimize garbage. All methods
* must be thread-safe.
*
* The design goals of this class are as follows:
*
* - Support a 'least surprising' math model to scripting language users. This
* means that exact, or decimal math should be used for default calculations. This
* scheme assumes that by default, groovy literals with decimal points are instantiated
* as BigDecimal objects rather than binary floating points (Float, Double).
*
- Do not force the appearance of exactness on a number that is by definition not
* guaranteed to be exact. In particular this means that if an operand in a NumberMath
* operation is a binary floating point number, ensure that the result remains a binary floating point
* number (i.e. never automatically promote a binary floating point number to a BigDecimal).
* This has the effect of preserving the expectations of binary floating point users and helps performance.
*
- Provide an implementation that is as close as practical to the Java 1.5 BigDecimal math model which implements
* precision based floating point decimal math (ANSI X3.274-1996 and ANSI X3.274-1996/AM 1-2000 (section 7.4).
*
* reference groovy source code
*/
public abstract class NumberMath {
public static Number abs(Number number) {
return getMath(number).absImpl(number);
}
public static Number add(Number left, Number right) {
return getMath(left, right).addImpl(left, right);
}
public static Number subtract(Number left, Number right) {
return getMath(left, right).subtractImpl(left, right);
}
public static Number multiply(Number left, Number right) {
return getMath(left, right).multiplyImpl(left, right);
}
public static Number divide(Number left, Number right) {
return getMath(left, right).divideImpl(left, right);
}
public static int compareTo(Number left, Number right) {
return getMath(left, right).compareToImpl(left, right);
}
public static Number or(Number left, Number right) {
return getMath(left, right).orImpl(left, right);
}
public static Number and(Number left, Number right) {
return getMath(left, right).andImpl(left, right);
}
public static Number xor(Number left, Number right) {
return getMath(left, right).xorImpl(left, right);
}
public static Number intDiv(Number left, Number right) {
return getMath(left, right).intDivImpl(left, right);
}
// retain for backwards compatibility
public static Number mod(Number left, Number right) {
return getMath(left, right).modImpl(left, right);
}
public static Number remainder(Number left, Number right) {
return getMath(left, right).remainderImpl(left, right);
}
/*
* For this operation, consider the operands independently. Throw an exception if the right operand
* (shift distance) is not an integral type. For the left operand (shift value) also require an integral
* type, but do NOT promote from Integer to Long. This is consistent with Java, and makes sense for the
* shift operators.
*/
public static Number leftShift(Number left, Number right) {
if (isFloatingPoint(right) || isBigDecimal(right)) {
throw new UnsupportedOperationException(
"Shift distance must be an integral type, but " + right + " (" + right.getClass().getName()
+ ") was supplied");
}
return getMath(left).leftShiftImpl(left, right);
}
/*
* For this operation, consider the operands independently. Throw an exception if the right operand
* (shift distance) is not an integral type. For the left operand (shift value) also require an integral
* type, but do NOT promote from Integer to Long. This is consistent with Java, and makes sense for the
* shift operators.
*/
public static Number rightShift(Number left, Number right) {
if (isFloatingPoint(right) || isBigDecimal(right)) {
throw new UnsupportedOperationException(
"Shift distance must be an integral type, but " + right + " (" + right.getClass().getName()
+ ") was supplied");
}
return getMath(left).rightShiftImpl(left, right);
}
/*
* For this operation, consider the operands independently. Throw an exception if the right operand
* (shift distance) is not an integral type. For the left operand (shift value) also require an integral
* type, but do NOT promote from Integer to Long. This is consistent with Java, and makes sense for the
* shift operators.
*/
public static Number rightShiftUnsigned(Number left, Number right) {
if (isFloatingPoint(right) || isBigDecimal(right)) {
throw new UnsupportedOperationException(
"Shift distance must be an integral type, but " + right + " (" + right.getClass().getName()
+ ") was supplied");
}
return getMath(left).rightShiftUnsignedImpl(left, right);
}
public static Number bitwiseNegate(Number left) {
return getMath(left).bitwiseNegateImpl(left);
}
public static Number unaryMinus(Number left) {
return getMath(left).unaryMinusImpl(left);
}
public static Number unaryPlus(Number left) {
return getMath(left).unaryPlusImpl(left);
}
public static boolean isFloatingPoint(Number number) {
return number instanceof Double || number instanceof Float;
}
public static boolean isInteger(Number number) {
return number instanceof Integer;
}
public static boolean isShort(Number number) {
return number instanceof Short;
}
public static boolean isByte(Number number) {
return number instanceof Byte;
}
public static boolean isLong(Number number) {
return number instanceof Long;
}
public static boolean isBigDecimal(Number number) {
return number instanceof BigDecimal;
}
public static boolean isBigInteger(Number number) {
return number instanceof BigInteger;
}
public static BigDecimal toBigDecimal(Number n) {
if (n instanceof BigDecimal) {
return (BigDecimal)n;
}
if (n instanceof BigInteger) {
return new BigDecimal((BigInteger)n);
}
if (n instanceof Integer || n instanceof Long || n instanceof Byte || n instanceof Short) {
return BigDecimal.valueOf(n.longValue());
}
try {
return new BigDecimal(n.toString());
} catch (NumberFormatException nfe) {
return BigDecimal.valueOf(n.doubleValue());
}
}
public static BigInteger toBigInteger(Number n) {
if (n instanceof BigInteger) {
return (BigInteger)n;
}
if (n instanceof Integer || n instanceof Long || n instanceof Byte || n instanceof Short) {
return BigInteger.valueOf(n.longValue());
}
if (n instanceof Float || n instanceof Double) {
BigDecimal bd = new BigDecimal(n.toString());
return bd.toBigInteger();
}
if (n instanceof BigDecimal) {
return ((BigDecimal)n).toBigInteger();
}
return new BigInteger(n.toString());
}
/*
* Determine which NumberMath instance to use, given the supplied operands. This method implements
* the type promotion rules discussed in the documentation. Note that by the time this method is
* called, any Byte, Character or Short operands will have been promoted to Integer. For reference,
* here is the promotion matrix:
* bD bI D F L I
* bD bD bD D D bD bD
* bI bD bI D D bI bI
* D D D D D D D
* F D D D D D D
* L bD bI D D L L
* I bD bI D D L I
*
* Note that for division, if either operand isFloatingPoint, the result will be floating. Otherwise,
* the result is BigDecimal
*/
public static NumberMath getMath(Number left, Number right) {
// FloatingPointMath wins according to promotion Matrix
if (isFloatingPoint(left) || isFloatingPoint(right)) {
return FloatingPointMath.INSTANCE;
}
NumberMath leftMath = getMath(left);
NumberMath rightMath = getMath(right);
if (leftMath == BigDecimalMath.INSTANCE || rightMath == BigDecimalMath.INSTANCE) {
return BigDecimalMath.INSTANCE;
}
if (leftMath == BigIntegerMath.INSTANCE || rightMath == BigIntegerMath.INSTANCE) {
return BigIntegerMath.INSTANCE;
}
if (leftMath == LongMath.INSTANCE || rightMath == LongMath.INSTANCE) {
return LongMath.INSTANCE;
}
if (leftMath == IntegerMath.INSTANCE || rightMath == IntegerMath.INSTANCE) {
return IntegerMath.INSTANCE;
}
// also for custom Number implementations
return BigDecimalMath.INSTANCE;
}
/* package private */
static NumberMath getMath(Number number) {
if (isLong(number)) {
return LongMath.INSTANCE;
}
if (isFloatingPoint(number)) {
return FloatingPointMath.INSTANCE;
}
if (isBigDecimal(number)) {
return BigDecimalMath.INSTANCE;
}
if (isBigInteger(number)) {
return BigIntegerMath.INSTANCE;
}
if (isInteger(number) || isShort(number) || isByte(number)) {
return IntegerMath.INSTANCE;
}
// also for custom Number implementations
return BigDecimalMath.INSTANCE;
}
//Subclasses implement according to the type promotion hierarchy rules
protected abstract Number absImpl(Number number);
public abstract Number addImpl(Number left, Number right);
public abstract Number subtractImpl(Number left, Number right);
public abstract Number multiplyImpl(Number left, Number right);
public abstract Number divideImpl(Number left, Number right);
public abstract int compareToImpl(Number left, Number right);
protected abstract Number unaryMinusImpl(Number left);
protected abstract Number unaryPlusImpl(Number left);
protected Number bitwiseNegateImpl(Number left) {
throw createUnsupportedException("bitwiseNegate()", left);
}
protected Number orImpl(Number left, Number right) {
throw createUnsupportedException("or()", left);
}
protected Number andImpl(Number left, Number right) {
throw createUnsupportedException("and()", left);
}
protected Number xorImpl(Number left, Number right) {
throw createUnsupportedException("xor()", left);
}
protected Number remainderImpl(Number left, Number right) {
throw createUnsupportedException("remainder()", left);
}
protected Number modImpl(Number left, Number right) {
throw createUnsupportedException("mod()", left);
}
protected Number intDivImpl(Number left, Number right) {
throw createUnsupportedException("intDiv()", left);
}
protected Number leftShiftImpl(Number left, Number right) {
throw createUnsupportedException("leftShift()", left);
}
protected Number rightShiftImpl(Number left, Number right) {
throw createUnsupportedException("rightShift()", left);
}
protected Number rightShiftUnsignedImpl(Number left, Number right) {
throw createUnsupportedException("rightShiftUnsigned()", left);
}
protected UnsupportedOperationException createUnsupportedException(String operation, Number left) {
return new UnsupportedOperationException(
"Cannot use " + operation + " on this number type: " + left.getClass().getName() + " with value: " + left);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy