Skip to content

Commit 3b403b0

Browse files
committed
fn:round, rounding -ve to 0, we must return -0
Changes for rounding of types (DoubleValue, FloatValue) which distinguish -0 from 0.
1 parent 05ff3d1 commit 3b403b0

File tree

3 files changed

+49
-5
lines changed

3 files changed

+49
-5
lines changed

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

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.exist.xquery.value.*;
2929

3030
import java.math.RoundingMode;
31+
import java.util.Objects;
3132

3233
/**
3334
* Base class for rounding mode functions
@@ -63,16 +64,46 @@ public Sequence eval(final Sequence[] args, final Sequence contextSequence) thro
6364
}
6465

6566
final RoundingMode roundingMode = getFunctionRoundingMode(value);
66-
67+
6768
if (args.length > 1) {
6869
final Item precisionItem = args[1].itemAt(0);
6970
if (precisionItem instanceof IntegerValue) {
7071
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;
72103
}
73104
}
74105

75-
return value.round(IntegerValue.ZERO, roundingMode);
106+
return rounded.convertTo(value.getType());
76107
}
77108

78109
}

exist-core/src/main/java/org/exist/xquery/value/DecimalValue.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ public boolean hasFractionalPart() {
209209
/* (non-Javadoc)
210210
* @see org.exist.xquery.value.Sequence#convertTo(int)
211211
*/
212+
@Override
212213
public AtomicValue convertTo(int requiredType) throws XPathException {
213214
switch (requiredType) {
214215
case Type.ATOMIC:

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,30 @@ function fr:round-half-to-even($number as xs:double) {
117117
declare
118118
%test:args("-0.41",1)
119119
%test:assertEquals("-0.4")
120-
%test:args("-0.41",0)
120+
%test:args("-0.43",0)
121121
%test:assertEquals("-0")
122+
%test:args("-0.43",0)
123+
%test:assertEquals("0")
124+
%test:args("-0.43",0)
125+
%test:assertEquals(0)
122126
function fr:round-negative-zero($number as xs:decimal, $precision as xs:integer) {
123127
fn:round($number, $precision)
124128
};
125129

126-
127130
declare
128131
%test:args("-0.41",1)
129132
%test:assertEquals("-0.4")
130133
%test:args("-0.41",0)
131134
%test:assertEquals("-0")
132135
function fr:round-negative-zero-double($number as xs:double, $precision as xs:integer) {
133136
fn:round($number, $precision)
137+
};
138+
139+
declare
140+
%test:args("-0.41",1)
141+
%test:assertEquals("-0.4")
142+
%test:args("-0.41",0)
143+
%test:assertEquals("-0")
144+
function fr:round-negative-zero-float($number as xs:float, $precision as xs:integer) {
145+
fn:round($number, $precision)
134146
};

0 commit comments

Comments
 (0)