XQuery, shared with XSLT and XPath 2.0 and later, supports various number data types, two of them are xs:double
and xs:decimal
. It is possible to cast an xs:double
to an xs:decimal
, as defined in http://www.w3.org/TR/xquery-operators/#casting-to-numerics.
Implementations done in Java seem to implement xs:double
using the Java double
data type and xs:decimal
using the java.math.BigDecimal
class. That class supports two ways of converting a double
into a BigDecimal
, namely doing BigDecimal.valueOf(doubleValue)
and new BigDecimal(doubleValue)
. According to https://stackoverflow.com/a/7186298/252228, the former gives the more intuitive result while the latter gives the more correct result, as for instance BigDecimal.valueOf(1.1)
results in 1.1
while new BigDecimal(1.1)
results in 1.100000000000000088817841970012523233890533447265625
.
When I try the cast of an xs:double
to an xs:decimal
with Saxon and Exist then
xquery version "1.0";
let $d1 as xs:double := 1.1E0
return xs:decimal($d1)
outputs 1.100000000000000088817841970012523233890533447265625
while with BaseX it outputs 1.1
. I suppose the difference results from the different implementations, BaseX doing BigDecimal.valueOf(1.1)
, Saxon and Exist doing new BigDecimal(1.1)
.
My question is: which approach is the right one to implement the cast operation according to the http://www.w3.org/TR/xquery-operators/#casting-to-numerics?
An implementation of xs:decimal is required to support at least 18 significant decimal digits, and if it does so, the closest xs:decimal to the xs:double value 1.1 will be something like 1.100 000 000 000 000 088 rather than 1.1. (That's actually 19 digits rather than 18, but even with 18, the difference from 1.1 should show up.) So I believe that returning the decimal 1.1 is non-conformant. The number of digits is implementation-defined beyond 18 digits, but the first 18 must be essentially as shown.
The actual code in Saxon is
public DecimalValue(double in) throws ValidationException {
try {
BigDecimal d = new BigDecimal(in);
value = d.stripTrailingZeros();
} catch (NumberFormatException err) {
//...
}
The XQuery Recommendation leaves it up to the implementation what the closest decimal representation of a double value is supposed to be. The reason is that the specification is supposed to support implementations in arbitrary programming languages.
However, thanks to your hint that BigDecimal.valueOf(d)
and new BigDecimal(d)
are not equivalent, the next release of BaseX (Version 8.0) will return the more accurate result.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With