Skip to content

Commit

Permalink
Extending BigDecimal to use any floating-point
Browse files Browse the repository at this point in the history
  • Loading branch information
svanteschubert committed Nov 14, 2020
1 parent fe8ca45 commit 70d0a11
Showing 1 changed file with 11 additions and 156 deletions.
167 changes: 11 additions & 156 deletions src/main/java/net/sf/saxon/value/BigDecimalValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -27,8 +26,6 @@

public final class BigDecimalValue extends DecimalValue {

public static final int DIVIDE_PRECISION = 18;

private BigDecimal value;
private Double doubleValue;

Expand All @@ -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
*
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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;
}

/**
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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";
}
Expand All @@ -347,7 +251,7 @@ public CharSequence getCanonicalLexicalRepresentation() {
/*@NotNull*/
@Override
public CharSequence getPrimitiveStringValue() {
return decimalToString(value, new FastStringBuffer(FastStringBuffer.C16));
return value.stripTrailingZeros().toPlainString();
}

/**
Expand All @@ -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());
}

/**
Expand Down Expand Up @@ -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
Expand All @@ -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));
}

}
Expand Down

0 comments on commit 70d0a11

Please sign in to comment.