Skip to content

Commit 5359a29

Browse files
committed
Fix fn:round, fn:round-hallf-to-even edge cases
1. Give back same answer on INF and NaN inputs 2. Use only as much precision as the value being rounded, to avoid overflows when a very big precision is input.
1 parent 3b403b0 commit 5359a29

File tree

2 files changed

+48
-1
lines changed

2 files changed

+48
-1
lines changed

exist-core/src/main/java/org/exist/xquery/functions/fn/FunRoundBase.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,15 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
8686
* @throws XPathException if a conversion goes wrong (it shouldn't)
8787
*/
8888
private static Sequence convertValue(final IntegerValue precision, final NumericValue value, final RoundingMode roundingMode) throws XPathException {
89+
90+
if (value.isInfinite() || value.isNaN()) {
91+
return value;
92+
}
93+
8994
final DecimalValue decimal = (DecimalValue)value.convertTo(Type.DECIMAL);
90-
final DecimalValue rounded = (DecimalValue) Objects.requireNonNull(decimal).round(precision, roundingMode);
95+
// (AP) This is as much precision as we can need, and prevents overflows scaling BigInteger
96+
final IntegerValue usePrecision = truncatePrecision(decimal, precision);
97+
final DecimalValue rounded = (DecimalValue) Objects.requireNonNull(decimal).round(usePrecision, roundingMode);
9198

9299
if (value.isNegative() && rounded.isZero()) {
93100
//Extreme care!! (AP) -0 as DecimalValue will not be negative, -0.0f and -0.0d will be negative.
@@ -106,4 +113,12 @@ private static Sequence convertValue(final IntegerValue precision, final Numeric
106113
return rounded.convertTo(value.getType());
107114
}
108115

116+
private static IntegerValue truncatePrecision(final DecimalValue decimal, final IntegerValue precision) throws XPathException {
117+
118+
final IntegerValue decimalPrecision = new IntegerValue(decimal.getValue().precision());
119+
if (decimalPrecision.compareTo(precision) < 0) {
120+
return decimalPrecision;
121+
}
122+
return precision;
123+
}
109124
}

exist-core/src/test/xquery/numbers/fn-round.xqm

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,20 @@ function fr:round-half-to-even($number as xs:double) {
114114
fn:round-half-to-even($number)
115115
};
116116

117+
declare
118+
%test:args("3.567812E+3", 4294967296)
119+
%test:assertEquals("3567.812")
120+
function fr:round-half-to-even-high-precision($number as xs:double, $precision as xs:integer) {
121+
fn:round-half-to-even($number, $precision)
122+
};
123+
124+
declare
125+
%test:args("-0.05", 1)
126+
%test:assertEquals("-0.1")
127+
function fr:round-half-to-even-inexact-halfway-float($number as xs:float, $precision as xs:integer) {
128+
fn:round-half-to-even($number, $precision)
129+
};
130+
117131
declare
118132
%test:args("-0.41",1)
119133
%test:assertEquals("-0.4")
@@ -143,4 +157,22 @@ declare
143157
%test:assertEquals("-0")
144158
function fr:round-negative-zero-float($number as xs:float, $precision as xs:integer) {
145159
fn:round($number, $precision)
160+
};
161+
162+
declare
163+
%test:args("INF")
164+
%test:assertEquals("INF")
165+
%test:args("-INF")
166+
%test:assertEquals("-INF")
167+
function fr:round-inf-float($number as xs:float) {
168+
fn:round($number)
169+
};
170+
171+
declare
172+
%test:args("INF")
173+
%test:assertEquals("INF")
174+
%test:args("-INF")
175+
%test:assertEquals("-INF")
176+
function fr:round-inf-double($number as xs:double) {
177+
fn:round($number)
146178
};

0 commit comments

Comments
 (0)