diff options
3 files changed, 36 insertions, 18 deletions
diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java index 8400a7c..6d768a1 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/BuiltinOperators.java @@ -17,6 +17,7 @@ limitations under the License. package com.healthmarketscience.jackcess.impl.expr; import java.math.BigDecimal; +import java.math.MathContext; import java.math.RoundingMode; import java.text.DateFormat; import java.util.Date; @@ -58,7 +59,8 @@ public class BuiltinOperators public static final Value ZERO_VAL = FALSE_VAL; public static final RoundingMode ROUND_MODE = RoundingMode.HALF_EVEN; - private static final int MAX_NUMERIC_SCALE = 28; + public static final MathContext MATH_CONTEXT = + new MathContext(28, ROUND_MODE); private enum CoercionType { SIMPLE(true, true), GENERAL(false, true), COMPARE(false, false); @@ -107,7 +109,7 @@ public class BuiltinOperators return toValue(-param1.getAsDouble()); case STRING: case BIG_DEC: - return toValue(param1.getAsBigDecimal().negate()); + return toValue(param1.getAsBigDecimal().negate(MATH_CONTEXT)); default: throw new EvalException("Unexpected type " + mathType); } @@ -137,7 +139,8 @@ public class BuiltinOperators case DOUBLE: return toValue(param1.getAsDouble() + param2.getAsDouble()); case BIG_DEC: - return toValue(param1.getAsBigDecimal().add(param2.getAsBigDecimal())); + return toValue(param1.getAsBigDecimal().add( + param2.getAsBigDecimal(), MATH_CONTEXT)); default: throw new EvalException("Unexpected type " + mathType); } @@ -165,7 +168,8 @@ public class BuiltinOperators case DOUBLE: return toValue(param1.getAsDouble() - param2.getAsDouble()); case BIG_DEC: - return toValue(param1.getAsBigDecimal().subtract(param2.getAsBigDecimal())); + return toValue(param1.getAsBigDecimal().subtract( + param2.getAsBigDecimal(), MATH_CONTEXT)); default: throw new EvalException("Unexpected type " + mathType); } @@ -190,7 +194,8 @@ public class BuiltinOperators case DOUBLE: return toValue(param1.getAsDouble() * param2.getAsDouble()); case BIG_DEC: - return toValue(param1.getAsBigDecimal().multiply(param2.getAsBigDecimal())); + return toValue(param1.getAsBigDecimal().multiply( + param2.getAsBigDecimal(), MATH_CONTEXT)); default: throw new EvalException("Unexpected type " + mathType); } @@ -253,6 +258,18 @@ public class BuiltinOperators Value.Type mathType = getMathTypePrecedence(param1, param2, CoercionType.GENERAL); + if(mathType == Value.Type.BIG_DEC) { + // see if we can handle the limited options supported for BigDecimal + // (must be a positive int exponent) + try { + BigDecimal result = param1.getAsBigDecimal().pow( + param2.getAsBigDecimal().intValueExact(), MATH_CONTEXT); + return toValue(result); + } catch(ArithmeticException ae) { + // fall back to general handling via doubles... + } + } + // jdk only supports general pow() as doubles, let's go with that double result = Math.pow(param1.getAsDouble(), param2.getAsDouble()); @@ -727,13 +744,12 @@ public class BuiltinOperators try { // see if string can be coerced to a number - BigDecimal num = strParam.getAsBigDecimal(); + strParam.getAsBigDecimal(); if(prefType.isNumeric()) { - // re-evaluate the numeric type choice based on the type of the parsed - // number - Value.Type numType = ((num.scale() > 0) ? - Value.Type.BIG_DEC : Value.Type.LONG); - prefType = getPreferredNumericType(numType, prefType); + // seems like when strings are coerced to numbers, they are usually + // doubles, unless the current context is decimal + prefType = ((prefType == Value.Type.BIG_DEC) ? + Value.Type.BIG_DEC : Value.Type.DOUBLE); } return prefType; } catch(NumberFormatException ignored) { @@ -748,7 +764,7 @@ public class BuiltinOperators } static BigDecimal divide(BigDecimal num, BigDecimal denom) { - return num.divide(denom, MAX_NUMERIC_SCALE, ROUND_MODE); + return num.divide(denom, MATH_CONTEXT); } static boolean isIntegral(double d) { diff --git a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java index b2a4546..33ab133 100644 --- a/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java +++ b/src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultNumberFunctions.java @@ -28,7 +28,7 @@ import static com.healthmarketscience.jackcess.impl.expr.DefaultFunctions.*; * * @author James Ahlborn */ -public class DefaultNumberFunctions +public class DefaultNumberFunctions { private DefaultNumberFunctions() {} @@ -36,7 +36,7 @@ public class DefaultNumberFunctions static void init() { // dummy method to ensure this class is loaded } - + public static final Function ABS = registerFunc(new Func1NullIsNull("Abs") { @Override protected Value eval1(EvalContext ctx, Value param1) { @@ -55,7 +55,8 @@ public class DefaultNumberFunctions return BuiltinOperators.toValue(Math.abs(param1.getAsDouble())); case STRING: case BIG_DEC: - return BuiltinOperators.toValue(param1.getAsBigDecimal().abs()); + return BuiltinOperators.toValue(param1.getAsBigDecimal().abs( + BuiltinOperators.MATH_CONTEXT)); default: throw new EvalException("Unexpected type " + mathType); } diff --git a/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java b/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java index f20179a..714421e 100644 --- a/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java +++ b/src/test/java/com/healthmarketscience/jackcess/impl/expr/ExpressionatorTest.java @@ -281,7 +281,8 @@ public class ExpressionatorTest extends TestCase assertEquals(100, eval("=-10^2")); assertEquals(-100, eval("=-(10)^2")); - assertEquals(-100, eval("=-\"10\"^2")); + assertEquals(-100d, eval("=-\"10\"^2")); + assertEquals(toBD(-98.9d), eval("=1.1+(-\"10\"^2)")); assertEquals(toBD(99d), eval("=-10E-1+10e+1")); assertEquals(toBD(-101d), eval("=-10E-1-10e+1")); @@ -294,8 +295,8 @@ public class ExpressionatorTest extends TestCase assertEquals("12foo", eval("=12 + \"foo\"")); assertEquals("foo12", eval("=\"foo\" + 12")); - assertEquals(37, eval("=\"25\" + 12")); - assertEquals(37, eval("=12 + \"25\"")); + assertEquals(37d, eval("=\"25\" + 12")); + assertEquals(37d, eval("=12 + \"25\"")); evalFail(("=12 - \"foo\""), RuntimeException.class); evalFail(("=\"foo\" - 12"), RuntimeException.class); |