Skip to content

Commit a8e72a0

Browse files
committed
[bugfix] Fix numerous issues with missing type promotion in numeric operations
1 parent 8b85462 commit a8e72a0

File tree

3 files changed

+40
-39
lines changed

3 files changed

+40
-39
lines changed

exist-core/src/main/java/org/exist/xquery/ValueComparison.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ protected Sequence genericCompare(Sequence contextSequence, Item contextItem) th
7676
final AtomicValue rv = rs.itemAt(0).atomize();
7777
final Collator collator = getCollator(contextSequence);
7878
return BooleanValue.valueOf(compareAtomic(collator, lv, rv, StringTruncationOperator.NONE, relation));
79-
}
79+
}
8080
throw new XPathException(this, ErrorCodes.XPTY0004, "Type error: sequence with more than one item is not allowed here");
8181
}
8282

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

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ public boolean isPositive() {
283283
} else if (other instanceof DoubleValue) {
284284
comparison = () -> value.compareTo(BigDecimal.valueOf(((DoubleValue)other).value));
285285
} else if (other instanceof FloatValue) {
286-
comparison = () -> value.compareTo(BigDecimal.valueOf(((FloatValue)other).value));
286+
final BigDecimal otherPromoted = new BigDecimal(Float.toString(((FloatValue)other).value));
287+
comparison = () -> value.compareTo(otherPromoted);
287288
} else {
288289
return null;
289290
}
@@ -361,10 +362,8 @@ public NumericValue round(final IntegerValue precision) throws XPathException {
361362
return round(precision, DecimalValue.DEFAULT_ROUNDING_MODE);
362363
}
363364

364-
/* (non-Javadoc)
365-
* @see org.exist.xquery.value.NumericValue#minus(org.exist.xquery.value.NumericValue)
366-
*/
367-
public ComputableValue minus(ComputableValue other) throws XPathException {
365+
@Override
366+
public ComputableValue minus(final ComputableValue other) throws XPathException {
368367
switch (other.getType()) {
369368
case Type.DECIMAL:
370369
return new DecimalValue(value.subtract(((DecimalValue) other).value));
@@ -375,10 +374,8 @@ public ComputableValue minus(ComputableValue other) throws XPathException {
375374
}
376375
}
377376

378-
/* (non-Javadoc)
379-
* @see org.exist.xquery.value.NumericValue#plus(org.exist.xquery.value.NumericValue)
380-
*/
381-
public ComputableValue plus(ComputableValue other) throws XPathException {
377+
@Override
378+
public ComputableValue plus(final ComputableValue other) throws XPathException {
382379
switch (other.getType()) {
383380
case Type.DECIMAL:
384381
return new DecimalValue(value.add(((DecimalValue) other).value));

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

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,8 @@ public boolean isPositive() {
137137
if (other instanceof IntegerValue) {
138138
comparison = () -> BigDecimal.valueOf(value).compareTo(new BigDecimal(((IntegerValue)other).value));
139139
} else if (other instanceof DecimalValue) {
140-
comparison = () -> BigDecimal.valueOf(value).compareTo(((DecimalValue)other).value);
140+
final BigDecimal promoted = new BigDecimal(Float.toString(value));
141+
comparison = () -> promoted.compareTo(((DecimalValue)other).value);
141142
} else if (other instanceof DoubleValue) {
142143
comparison = () -> BigDecimal.valueOf(value).compareTo(BigDecimal.valueOf(((DoubleValue)other).value));
143144
} else if (other instanceof FloatValue) {
@@ -276,35 +277,38 @@ public NumericValue round(IntegerValue precision) throws XPathException {
276277
return round(precision, DecimalValue.DEFAULT_ROUNDING_MODE);
277278
}
278279

279-
/* (non-Javadoc)
280-
* @see org.exist.xquery.value.NumericValue#minus(org.exist.xquery.value.NumericValue)
281-
*/
282-
public ComputableValue minus(ComputableValue other) throws XPathException {
280+
@Override
281+
public ComputableValue minus(final ComputableValue other) throws XPathException {
283282
if (Type.subTypeOf(other.getType(), Type.FLOAT)) {
284283
return new FloatValue(value - ((FloatValue) other).value);
284+
} else if (other.getType() == Type.DOUBLE) {
285+
// type promotion - see https://www.w3.org/TR/xpath-31/#promotion
286+
return ((DoubleValue) convertTo(Type.DOUBLE)).minus(other);
285287
} else {
286288
return minus((ComputableValue) other.convertTo(getType()));
287289
}
288290
}
289291

290-
/* (non-Javadoc)
291-
* @see org.exist.xquery.value.NumericValue#plus(org.exist.xquery.value.NumericValue)
292-
*/
293-
public ComputableValue plus(ComputableValue other) throws XPathException {
292+
@Override
293+
public ComputableValue plus(final ComputableValue other) throws XPathException {
294294
if (Type.subTypeOf(other.getType(), Type.FLOAT)) {
295295
return new FloatValue(value + ((FloatValue) other).value);
296+
} else if (other.getType() == Type.DOUBLE) {
297+
// type promotion - see https://www.w3.org/TR/xpath-31/#promotion
298+
return ((DoubleValue) convertTo(Type.DOUBLE)).plus(other);
296299
} else {
297300
return plus((ComputableValue) other.convertTo(getType()));
298301
}
299302
}
300303

301-
/* (non-Javadoc)
302-
* @see org.exist.xquery.value.NumericValue#mult(org.exist.xquery.value.NumericValue)
303-
*/
304-
public ComputableValue mult(ComputableValue other) throws XPathException {
304+
@Override
305+
public ComputableValue mult(final ComputableValue other) throws XPathException {
305306
switch (other.getType()) {
306307
case Type.FLOAT:
307308
return new FloatValue(value * ((FloatValue) other).value);
309+
case Type.DOUBLE:
310+
// type promotion - see https://www.w3.org/TR/xpath-31/#promotion
311+
return ((DoubleValue) convertTo(Type.DOUBLE)).mult(other);
308312
case Type.DAY_TIME_DURATION:
309313
case Type.YEAR_MONTH_DURATION:
310314
return other.mult(this);
@@ -358,26 +362,26 @@ public ComputableValue div(ComputableValue other) throws XPathException {
358362
}
359363
}
360364

361-
public IntegerValue idiv(NumericValue other) throws XPathException {
362-
final ComputableValue result = div(other);
363-
return new IntegerValue(((IntegerValue) result.convertTo(Type.INTEGER)).getLong());
364-
/*
365-
if (Type.subTypeOf(other.getType(), Type.FLOAT)) {
366-
float result = value / ((FloatValue) other).value;
367-
if (result == Float.NaN || result == Float.POSITIVE_INFINITY || result == Float.NEGATIVE_INFINITY)
368-
throw new XPathException("illegal arguments to idiv");
369-
return new IntegerValue(new BigDecimal(result).toBigInteger(), Type.INTEGER);
370-
}
371-
throw new XPathException("idiv called with incompatible argument type: " + getType() + " vs " + other.getType());
372-
*/
365+
@Override
366+
public IntegerValue idiv(final NumericValue other) throws XPathException {
367+
if (other.getType() == Type.DOUBLE) {
368+
// type promotion - see https://www.w3.org/TR/xpath-31/#promotion
369+
return ((DoubleValue) convertTo(Type.DOUBLE)).idiv(other);
370+
} else if (other.getType() == Type.DECIMAL) {
371+
return idiv((FloatValue) other.convertTo(Type.FLOAT));
372+
} else {
373+
final ComputableValue result = div(other);
374+
return new IntegerValue(((IntegerValue) result.convertTo(Type.INTEGER)).getLong());
375+
}
373376
}
374377

375-
/* (non-Javadoc)
376-
* @see org.exist.xquery.value.NumericValue#mod(org.exist.xquery.value.NumericValue)
377-
*/
378-
public NumericValue mod(NumericValue other) throws XPathException {
378+
@Override
379+
public NumericValue mod(final NumericValue other) throws XPathException {
379380
if (Type.subTypeOf(other.getType(), Type.FLOAT)) {
380381
return new FloatValue(value % ((FloatValue) other).value);
382+
} else if (other.getType() == Type.DOUBLE) {
383+
// type promotion - see https://www.w3.org/TR/xpath-31/#promotion
384+
return ((DoubleValue) convertTo(Type.DOUBLE)).mod(other);
381385
} else {
382386
return mod((NumericValue) other.convertTo(getType()));
383387
}

0 commit comments

Comments
 (0)