Number rounding and precision problems in XSLT 1.0 Number rounding and precision problems in XSLT 1.0 xml xml

Number rounding and precision problems in XSLT 1.0


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>