Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Number rounding and precision problems in XSLT 1.0

Tags:

xml

xslt

I have an inconsistency while using xsl,

here is the xml,

<Rate>
    <TotalRate>506.41</TotalRate>
    <TotalTax>17</TotalTax>
    <Currency>INR</Currency>
</Rate>

and xsl,

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:template match="/">
        <TotalAmount>
            <xsl:value-of select="Rate/TotalRate + Rate/TotalTax"/>
        </TotalAmount>
    </xsl:template>
</xsl:stylesheet>

and the output is,

<TotalAmount xmlns:fo="http://www.w3.org/1999/XSL/Format">523.4100000000001</TotalAmount>

but the expected o/p is,

<TotalAmount xmlns:fo="http://www.w3.org/1999/XSL/Format">523.41</TotalAmount>

Why the o/p is 523.4100000000001? how can i get 523.41 without rounding it?

like image 236
Sujit Avatar asked Oct 05 '12 12:10

Sujit


People also ask

How do you round a number in XSLT?

You can round to the nearest integer (round), always round up (ceiling) (), or always round down (floor). Figure 4.13. In this XML excerpt, you can see that the image's size is 528 pixels wide by 349 pixels tall. x s l t ...

Is XSLT 2.0 backward compatibility?

The XSLT 2.0 engine is backwards compatible. The only time the backwards compatibility of the XSLT 2.0 engine comes into effect is when using the XSLT 2.0 engine to process an XSLT 1.0 stylesheet.

What is number () in XSLT?

Specifies the format pattern. Here are some of the characters used in the formatting pattern: 0 (Digit) # (Digit, zero shows as absent)

Is xsl and XSLT the same?

XSL Transformation (XSLT)XSLT is designed to be used as part of XSL. In addition to XSLT, XSL includes an XML vocabulary for specifying formatting. XSL specifies the styling of an XML document by using XSLT to describe how the document is transformed into another XML document that uses the formatting vocabulary.


1 Answers

In XSLT 1.0 numbers are implemented with the double type and as with any binary floating-point type, there is a loss of precision.

In XSLT 2.0/XPath 2.0 one can use the xs:decimal type to work without loss of precision.


I. XSLT 1.0 solution:

Use the format-number() function:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:template match="/*">
        <TotalAmount>
      <xsl:value-of select="format-number(TotalRate + TotalTax, '0.##')"/>      
        </TotalAmount>
    </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<Rate>
    <TotalRate>506.41</TotalRate>
    <TotalTax>17</TotalTax>
    <Currency>INR</Currency>
</Rate>

the wanted, correct result is produced:

<TotalAmount>523.41</TotalAmount>

Here is also an example, showing that the wanted precision maynot be statically known and could be passed to the transformation as an external/global parameter:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:param name="pPrec" select="2"/>
    <xsl:param name="pPrec2" select="13"/>

    <xsl:variable name="vPict" select="'##################'"/>

    <xsl:template match="/*">
        <TotalAmount>
      <xsl:value-of select=
      "format-number(TotalRate + TotalTax,
                     concat('0.', substring($vPict,1,$pPrec))
                     )"/>
        </TotalAmount>
        <TotalAmount>
      <xsl:value-of select=
      "format-number(TotalRate + TotalTax,
                     concat('0.', substring($vPict,1,$pPrec2))
                     )"/>
        </TotalAmount>
    </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document, two results are produced -- with precision 2 and precision 13:

<TotalAmount>523.41</TotalAmount>
<TotalAmount>523.4100000000001</TotalAmount>

II. XSLT 2.0 solution using xs:decimal:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/*">
        <TotalAmount>
      <xsl:value-of select="xs:decimal(TotalRate) + xs:decimal(TotalTax)"/>
        </TotalAmount>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the same XML document (above), the wanted, correct result is produced:

<TotalAmount>523.41</TotalAmount>
like image 190
Dimitre Novatchev Avatar answered Oct 21 '22 14:10

Dimitre Novatchev