From 70d0a1197e298eb17dacf343553a2873352f2db2 Mon Sep 17 00:00:00 2001 From: Svante Schubert Date: Sat, 14 Nov 2020 22:11:02 +0100 Subject: [PATCH] Extending BigDecimal to use any floating-point --- .../net/sf/saxon/value/BigDecimalValue.java | 167 ++---------------- 1 file changed, 11 insertions(+), 156 deletions(-) diff --git a/src/main/java/net/sf/saxon/value/BigDecimalValue.java b/src/main/java/net/sf/saxon/value/BigDecimalValue.java index 4ec0b9b..312b3e2 100644 --- a/src/main/java/net/sf/saxon/value/BigDecimalValue.java +++ b/src/main/java/net/sf/saxon/value/BigDecimalValue.java @@ -14,9 +14,8 @@ import net.sf.saxon.type.*; import java.math.BigDecimal; -import java.math.BigInteger; +import java.math.MathContext; import java.math.RoundingMode; -import java.util.regex.Pattern; /** * An implementation class for decimal values other than integers @@ -27,8 +26,6 @@ public final class BigDecimalValue extends DecimalValue { - public static final int DIVIDE_PRECISION = 18; - private BigDecimal value; private Double doubleValue; @@ -52,8 +49,6 @@ public BigDecimalValue(BigDecimal value) { typeLabel = BuiltInAtomicType.DECIMAL; } - private static final Pattern decimalPattern = Pattern.compile("(\\-|\\+)?((\\.[0-9]+)|([0-9]+(\\.[0-9]*)?))"); - /** * Factory method to construct a DecimalValue from a string * @@ -86,92 +81,8 @@ public static ConversionResult makeDecimalValue(CharSequence in, boolean validat */ public static BigDecimalValue parse(CharSequence in) throws NumberFormatException { - FastStringBuffer digits = new FastStringBuffer(in.length()); - int scale = 0; - int state = 0; - // 0 - in initial whitespace; 1 - after sign - // 3 - after decimal point; 5 - in final whitespace - boolean foundDigit = false; - int len = in.length(); - for (int i = 0; i < len; i++) { - char c = in.charAt(i); - switch (c) { - case ' ': - case '\t': - case '\r': - case '\n': - if (state != 0) { - state = 5; - } - break; - case '+': - if (state != 0) { - throw new NumberFormatException("unexpected sign"); - } - state = 1; - break; - case '-': - if (state != 0) { - throw new NumberFormatException("unexpected sign"); - } - state = 1; - digits.cat(c); - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (state == 0) { - state = 1; - } else if (state >= 3) { - scale++; - } - if (state == 5) { - throw new NumberFormatException("contains embedded whitespace"); - } - digits.cat(c); - foundDigit = true; - break; - case '.': - if (state == 5) { - throw new NumberFormatException("contains embedded whitespace"); - } - if (state >= 3) { - throw new NumberFormatException("more than one decimal point"); - } - state = 3; - break; - default: - throw new NumberFormatException("invalid character '" + c + "'"); - } - - } - - if (!foundDigit) { - throw new NumberFormatException("no digits in value"); - } - - // remove insignificant trailing zeroes - while (scale > 0) { - if (digits.charAt(digits.length() - 1) == '0') { - digits.setLength(digits.length() - 1); - scale--; - } else { - break; - } - } - if (digits.isEmpty() || (digits.length() == 1 && digits.charAt(0) == '-')) { - return BigDecimalValue.ZERO; - } - BigInteger bigInt = new BigInteger(digits.toString()); - BigDecimal bigDec = new BigDecimal(bigInt, scale); - return new BigDecimalValue(bigDec); + BigDecimal bigDec = new BigDecimal(in.toString(), MathContext.DECIMAL128); + return new BigDecimalValue(bigDec); } /** @@ -182,8 +93,7 @@ public static BigDecimalValue parse(CharSequence in) throws NumberFormatExceptio */ public static boolean castableAsDecimal(CharSequence in) { - CharSequence trimmed = Whitespace.trimWhitespace(in); - return decimalPattern.matcher(trimmed).matches(); + return Boolean.TRUE; } /** @@ -195,7 +105,7 @@ public static boolean castableAsDecimal(CharSequence in) { public BigDecimalValue(double in) throws ValidationException { try { - BigDecimal d = new BigDecimal(in); + BigDecimal d = BigDecimal.valueOf(in); value = d.stripTrailingZeros(); } catch (NumberFormatException err) { // Must be a special value such as NaN or infinity @@ -300,13 +210,7 @@ public BigDecimal getDecimalValue() { */ public int hashCode() { - BigDecimal round = value.setScale(0, RoundingMode.DOWN); - long value = round.longValue(); - if (value > Integer.MIN_VALUE && value < Integer.MAX_VALUE) { - return (int) value; - } else { - return Double.valueOf(getDoubleValue()).hashCode(); - } + return value.hashCode(); } @Override @@ -331,7 +235,7 @@ public boolean effectiveBooleanValue() { @Override public CharSequence getCanonicalLexicalRepresentation() { - String s = getStringValue(); + String s = value.stripTrailingZeros().toPlainString(); if (s.indexOf('.') < 0) { s += ".0"; } @@ -347,7 +251,7 @@ public CharSequence getCanonicalLexicalRepresentation() { /*@NotNull*/ @Override public CharSequence getPrimitiveStringValue() { - return decimalToString(value, new FastStringBuffer(FastStringBuffer.C16)); + return value.stripTrailingZeros().toPlainString(); } /** @@ -359,51 +263,7 @@ public CharSequence getPrimitiveStringValue() { */ public static FastStringBuffer decimalToString(BigDecimal value, FastStringBuffer fsb) { - // Can't use BigDecimal#toString() under JDK 1.5 because this produces values like "1E-5". - // Can't use BigDecimal#toPlainString() because it retains trailing zeroes to represent the scale - int scale = value.scale(); - if (scale == 0) { - fsb.append(value.toString()); - return fsb; - } else if (scale < 0) { - String s = value.abs().unscaledValue().toString(); - if (s.equals("0")) { - fsb.cat('0'); - return fsb; - } - //FastStringBuffer sb = new FastStringBuffer(s.length() + (-scale) + 2); - if (value.signum() < 0) { - fsb.cat('-'); - } - fsb.append(s); - for (int i = 0; i < -scale; i++) { - fsb.cat('0'); - } - return fsb; - } else { - String s = value.abs().unscaledValue().toString(); - if (s.equals("0")) { - fsb.cat('0'); - return fsb; - } - int len = s.length(); - //FastStringBuffer sb = new FastStringBuffer(len+1); - if (value.signum() < 0) { - fsb.cat('-'); - } - if (scale >= len) { - fsb.append("0."); - for (int i = len; i < scale; i++) { - fsb.cat('0'); - } - fsb.append(s); - } else { - fsb.append(s.substring(0, len - scale)); - fsb.cat('.'); - fsb.append(s.substring(len - scale)); - } - return fsb; - } + return fsb.cat(value.stripTrailingZeros().toPlainString()); } /** @@ -434,7 +294,7 @@ public NumericValue ceiling() { } /** - * Implement the XPath round() function + * Does NOT implement the XPath round() function but HALF-UP rounding in accordance to basic commerce rules */ @Override @@ -452,15 +312,10 @@ public NumericValue round(int scale) { } switch (value.signum()) { - case -1: - return new BigDecimalValue(value.setScale(scale, RoundingMode.HALF_DOWN)); case 0: return this; - case +1: - return new BigDecimalValue(value.setScale(scale, RoundingMode.HALF_UP)); default: - // can't happen - return this; + return new BigDecimalValue(value.setScale(scale, RoundingMode.HALF_UP)); } }