|
28 | 28 | import org.exist.xquery.value.*;
|
29 | 29 |
|
30 | 30 | import java.math.RoundingMode;
|
| 31 | +import java.util.Objects; |
31 | 32 |
|
32 | 33 | /**
|
33 | 34 | * Base class for rounding mode functions
|
@@ -63,16 +64,46 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
|
63 | 64 | }
|
64 | 65 |
|
65 | 66 | final RoundingMode roundingMode = getFunctionRoundingMode(value);
|
66 |
| - |
| 67 | + |
67 | 68 | if (args.length > 1) {
|
68 | 69 | final Item precisionItem = args[1].itemAt(0);
|
69 | 70 | if (precisionItem instanceof IntegerValue) {
|
70 | 71 | final IntegerValue precision = (IntegerValue) precisionItem;
|
71 |
| - return value.round(precision, roundingMode); |
| 72 | + return convertValue(precision, value, roundingMode); |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + return convertValue(IntegerValue.ZERO, value, roundingMode); |
| 77 | + } |
| 78 | + |
| 79 | + /** |
| 80 | + * Apply necessary conversions to/from decimal to perform rounding in decimal |
| 81 | + * |
| 82 | + * @param precision precision of rounding |
| 83 | + * @param value to round |
| 84 | + * @param roundingMode mode to round in |
| 85 | + * @return rounded value in decimal converted back to the input type |
| 86 | + * @throws XPathException if a conversion goes wrong (it shouldn't) |
| 87 | + */ |
| 88 | + private static Sequence convertValue(final IntegerValue precision, final NumericValue value, final RoundingMode roundingMode) throws XPathException { |
| 89 | + final DecimalValue decimal = (DecimalValue)value.convertTo(Type.DECIMAL); |
| 90 | + final DecimalValue rounded = (DecimalValue) Objects.requireNonNull(decimal).round(precision, roundingMode); |
| 91 | + |
| 92 | + if (value.isNegative() && rounded.isZero()) { |
| 93 | + //Extreme care!! (AP) -0 as DecimalValue will not be negative, -0.0f and -0.0d will be negative. |
| 94 | + //So we need to test that original value to decide whether to negate a "zero" result. |
| 95 | + //DecimalValue(s) are not necessarily normalized, but the 0-test will work.. |
| 96 | + switch (value.getType()) { |
| 97 | + case Type.DOUBLE: |
| 98 | + return new DoubleValue(-0.0d); |
| 99 | + case Type.FLOAT: |
| 100 | + return new FloatValue(-0.0f); |
| 101 | + default: |
| 102 | + break; |
72 | 103 | }
|
73 | 104 | }
|
74 | 105 |
|
75 |
| - return value.round(IntegerValue.ZERO, roundingMode); |
| 106 | + return rounded.convertTo(value.getType()); |
76 | 107 | }
|
77 | 108 |
|
78 | 109 | }
|
0 commit comments