Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should cast of xs:double to xs:decimal be implemented as BigDecimal.valueOf(double) or new BigDecimal(double)?

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?

like image 945
Martin Honnen Avatar asked Feb 01 '15 15:02

Martin Honnen


2 Answers

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) {
            //...
        }
like image 158
Michael Kay Avatar answered Oct 21 '22 02:10

Michael Kay


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.

like image 25
Christian Grün Avatar answered Oct 21 '22 03:10

Christian Grün