0

I have an XSLT as below:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>

<xsl:template match="Test">

        <Result>
            <xsl:value-of select="number(depth)*number(width)*number(height)"/>
        </Result>


</xsl:template>

When I test this XSLT against the below sample file in Altova XML or in W3CSchool here, I get the result as 0.128

Sample file:

<?xml version="1.0" encoding="UTF-8"?>
<Test>
<depth>.8</depth>
<width>.8</width>
<height>.2</height>
</Test>

However, things change when I use Java to invoke the XSLT. I get the result as

<Result>0.12800000000000003</Result>

Below is the simple code I'm using:

 import javax.xml.transform.*;
    import javax.xml.transform.stream.StreamResult;
    import javax.xml.transform.stream.StreamSource;
    import java.io.File;
    import java.io.IOException;
    import java.net.URISyntaxException;

public class TestMain {
    public static void main(String[] args) throws IOException, URISyntaxException, TransformerException {
        TransformerFactory factory = TransformerFactory.newInstance();
        Source xslt = new StreamSource(new File("transform.xslt"));
        Transformer transformer = factory.newTransformer(xslt);

        Source text = new StreamSource(new File("input.xml"));
        transformer.transform(text, new StreamResult(new File("output.xml")));
    }
}

Question: Why is the Java code giving the output as 0.12800000000000003? Even 0.12800000000000000 is understandable, but 0.12800000000000003 is incorrect calculation.

nitgeek
  • 1,250
  • 12
  • 20
  • 1
    Possible duplicate of [Why Are Floating Point Numbers Inaccurate?](http://stackoverflow.com/questions/21895756/why-are-floating-point-numbers-inaccurate) – Arnav Borborah Sep 07 '16 at 16:15
  • See: http://stackoverflow.com/questions/37400663/decimal-precision-in-xslt/37407426#37407426 – michael.hor257k Sep 07 '16 at 16:16
  • @michael.hor257k Understood. But, it's XSLT V2. So, Altova and Java program should give the same result unless we say above java code doesn't use XSLT V2 but V1? – nitgeek Sep 07 '16 at 16:25
  • 1
    @nitgeek, are you sure you use an XSLT 2.0 processor? Oracle's Java JDK or JRE only supports XSLT 1.0 so unless you put Saxon 9 on the class path you are not using XSLT 2.0. – Martin Honnen Sep 07 '16 at 16:28
  • No, I am afraid you haven't understood it at all. You need to cast your numbers to **decimal** using `` instead of `` which doesn't really do anything. And as just noted, it requires an XSLT 2.0 procesor. – michael.hor257k Sep 07 '16 at 16:31
  • @michael.hor257k I did understand that. What I didn't get was why different results from Altova and Java. Thanks for the help. – nitgeek Sep 07 '16 at 16:34
  • Different processors may produce different results. I get 0.128 too when I run this on libxslt, a non-Java XSLT 1.0 processor. – michael.hor257k Sep 07 '16 at 16:36

1 Answers1

2

Firstly, floating point arithmetic will in general produce such rounding errors, because numbers like 0.8 cannot be represented accurately in the value space of xs:double.

Secondly, your stylesheet is explicitly using the number() function, which converts values in the source document (like 0.8) to floating point, both in XSLT 1.0 and XSLT 2.0. XSLT 2.0 offers a solution in that you could replace the call to number() by a call on xs:decimal(), which would give you decimal arithmetic rather than binary floating-point, and thus avoid the rounding errors. But the code you are currently executing is doing floating-point arithmetic in both cases.

The correct answer to this expression according to the rules of the W3C specification in both 1.0 and 2.0 is in fact 0.12800000000000003. The specification does not give any leniency on this. But implementors take short-cuts, and use libraries for floating-point arithmetic (and more particularly, for number-to-string conversion) that were not written to follow the W3C rules. I would strongly suspect that an implementation that outputs 0.128 for this query is using a number-to-string conversion routine that is trying to be smarter than the W3C spec allows.

If you want to avoid this kind of rounding error, the correct approach is:

(a) with XSLT 1.0, use format-number() to format the output to the number of decimal places that are likely to be accurate (or that are actually needed)

(b) with XSLT 2.0, use xs:decimal arithmetic - which when you are reading numbers from the source document means explicitly making them xs:decimal, either by validating the source document against a schema that declares the type as xs:decimal, or by using the xs:decimal() function in the stylesheet.

Michael Kay
  • 156,231
  • 11
  • 92
  • 164