Number rounding and precision problems in XSLT 1.0

后端 未结 1 1979
无人共我
无人共我 2020-12-30 06:44

I have an inconsistency while using xsl,

here is the xml,


    506.41
    17

        
相关标签:
1条回答
  • 2020-12-30 07:47

    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>
    
    0 讨论(0)
提交回复
热议问题