I am using xslt 2.0 and Saxon 9.6, and need to multiply a price amount and quantity, and then round the result to two decimals.I do not want banker's rounding, I want to round so .495 = 0.50 and .494 = .49. I have tried read several articles and posts regarding this issue, but I cannot find the solution. I see several places that rounding with xslt 1.0 is problematic because of the floating point issue, and a lot of mentions that the xslt 2.0 and the xs:decimal should do the trick, but I cannot seem to find a "waterproof" solution.
I have an xml file (Invoice) with 4 different InvoiceLines containing a price and a quantity element:
<Invoice>
<ID>12345</ID>
<IssueDate>2012-11-21</IssueDate>
<Supplier>
<Party>
<ID>977187761</ID>
</Party>
</Supplier>
<Customer>
<Party>
<ID schemeID="NO:ORGNR">810305282</ID>
</Party>
</Customer>
<Delivery>
<DeliveryDate>2012-11-21</DeliveryDate>
</Delivery>
<TaxTotal>
<TaxAmount currencyID="NOK">128.89</TaxAmount>
</TaxTotal>
<InvoiceLine>
<ID>1</ID>
<Quantity unitCode="EA">19</Quantity>
<LineAmount currencyID="NOK">130.26</LineAmount>
<Item>
<Name>TestItem</Name>
</Item>
<Price currencyID="NOK">8.569736842105263</Price>
</InvoiceLine>
<InvoiceLine>
<ID>2</ID>
<Quantity unitCode="NAR">1.00</Quantity>
<LineAmount currencyID="NOK">128.2</LineAmount>
<Item>
<Name>Vare A</Name>
</Item>
<Price currencyID="NOK">128.195</Price>
</InvoiceLine>
<InvoiceLine>
<ID>3</ID>
<Quantity unitCode="NAR">1.00</Quantity>
<LineAmount currencyID="NOK">128.7</LineAmount>
<Item>
<Name>Vare B</Name>
</Item>
<Price currencyID="NOK">128.695</Price>
</InvoiceLine>
<InvoiceLine>
<ID>4</ID>
<Quantity unitCode="NAR">1.00</Quantity>
<LineAmount currencyID="NOK">128.4</LineAmount>
<Item>
<Name>Vare C</Name>
</Item>
<Price currencyID="NOK">128.395</Price>
</InvoiceLine>
I have run the following xslt on the xml:
<xsl:template match="/">
<html>
<body>
<xsl:for-each select="//InvoiceLine">
<tr>
<td>Line: <xsl:value-of select="ID"/> </td>
<td>Price: <xsl:value-of select="Price"/></td>
<td>Quantity: <xsl:value-of select="Quantity"/></td>
<td>LineAmount: <xsl:value-of select="LineAmount"/></td>
<td>P*Q: <xsl:value-of select="Price * Quantity"/></td>
<td>Round(p*q*100)div 100: <xsl:value-of select="round((Price * Quantity * 100)) div 100" /></td>
<td>xs:decimal(p*q): <xsl:value-of select="xs:decimal(Price * Quantity)" /></td>
<td>round((p*q)*10*10) div 100: <xsl:value-of select="round((Price * Quantity) * 10 * 10) div 100"/></td>
</tr>
</xsl:for-each>
</body>
</html>
</xsl:template>
And this is the result:
<html xmlns:saxon="http://saxon.sf.net/" xmlns:op="http://www.w3.org/2002/08/xquery-operators" xmlns:schold="http://www.ascc.net/xml/schematron" xmlns:iso="http://purl.oclc.org/dsdl/schematron" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2">
<body>
<tr>
<td>Line: 1</td>
<td>Price: 8.569736842105263</td>
<td>Quantity: 19</td>
<td>LineAmount: 130.26</td>
<td>P*Q: 162.825</td>
<td>Round(p*q*100)div 100: 162.82</td>
<td>xs:decimal(p*q): 162.82499999999998863131622783839702606201171875</td>
<td>round((p*q)*10*10) div 100: 162.83</td>
</tr>
<tr>
<td>Line: 2</td>
<td>Price: 128.195</td>
<td>Quantity: 1.00</td>
<td>LineAmount: 128.2</td>
<td>P*Q: 128.195</td>
<td>Round(p*q*100)div 100: 128.2</td>
<td>xs:decimal(p*q): 128.19499999999999317878973670303821563720703125</td>
<td>round((p*q)*10*10) div 100: 128.19</td>
</tr>
<tr>
<td>Line: 3</td>
<td>Price: 128.695</td>
<td>Quantity: 1.00</td>
<td>LineAmount: 128.7</td>
<td>P*Q: 128.695</td>
<td>Round(p*q*100)div 100: 128.7</td>
<td>xs:decimal(p*q): 128.69499999999999317878973670303821563720703125</td>
<td>round((p*q)*10*10) div 100: 128.69</td>
</tr>
<tr>
<td>Line: 4</td>
<td>Price: 128.395</td>
<td>Quantity: 1.00</td>
<td>LineAmount: 128.4</td>
<td>P*Q: 128.395</td>
<td>Round(p*q*100)div 100: 128.4</td>
<td>xs:decimal(p*q): 128.395000000000010231815394945442676544189453125</td>
<td>round((p*q)*10*10) div 100: 128.4</td>
</tr>
As you can see, I get different results of the different types of calculations/rounding. For line 1 I get the exptected result in the last calculation, but for line 2 and 3, this calculation does not give me the exptected result.
I would be very greatful for any tips on how to solve this.
How about:
<xsl:value-of select="format-number(Price * Quantity, '0.00')"/>
Note also that I get the same - expected - results with all of these:
<xsl:value-of select="round(Price * Quantity * 100) div 100" />
and:
<xsl:value-of select="round(xs:decimal(Price) * xs:decimal(Quantity) * 100) div 100" />
and:
<xsl:value-of select="round(xs:decimal(Price) * xs:decimal(Quantity) * 10 * 10) div 100"/>
If you don't want "banker's rounding", then you cannot use the format-number()
function, since it does the same rounding as round-half-to-even()
does - see point #5 here: http://www.w3.org/TR/xslt20/#formatting-the-number
However, all the other methods do the expected rounding to the nearest integer.