【问题标题】:XSLT 2.0: Rounding result of calculation to two decimalsXSLT 2.0:将计算结果四舍五入到小数点后两位
【发布时间】:2015-06-30 09:51:10
【问题描述】:

我使用的是 xslt 2.0 和 Saxon 9.6,需要将价格金额和数量相乘,然后将结果四舍五入到小数点后两位。我不想要银行家的四舍五入,我想四舍五入所以 .495 = 0.50 和 .494 = .49。我已尝试阅读有关此问题的几篇文章和帖子,但找不到解决方案。我看到有几个地方因为浮点问题而使用 xslt 1.0 舍入是有问题的,并且很多人提到 xslt 2.0 和 xs:decimal 应该可以解决问题,但我似乎找不到“防水”解决方案。

我有一个包含价格和数量元素的 4 个不同 InvoiceLines 的 xml 文件(发票):

<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>

我在 xml 上运行了以下 xslt:

<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>

结果如下:

<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>

如您所见,我得到不同类型的计算/舍入的不同结果。对于第 1 行,我在最后一次计算中得到了预期的结果,但对于第 2 行和第 3 行,这个计算并没有给我预期的结果。

如果有任何解决此问题的提示,我将不胜感激。

【问题讨论】:

  • 你能把输入的内容贴出来吗。
  • 我刚刚用输入 xml 编辑了我的问题

标签: xslt rounding xslt-2.0 xpath-2.0 rounding-error


【解决方案1】:

我假设您没有使用模式感知,这意味着价格和数量等值是无类型的原子。如果您使用 untypedAtomic 值作为算术运算符的输入,则将其视为双精度浮点数。因此,您应该做的第一件事是显式转换为 xs:decimal:您的代码 xs:decimal(Price * Quantity) 使用双精度运算进行乘法运算,并将结果转换为十进制,而 xs:decimal(Price) * xs:decimal(Quantity) 将使用十进制运算。

如果您在进行任何算术运算之前确保将未键入的值全部转换为十进制,则结果的舍入和格式化应该“按预期”工作。

【讨论】:

  • 但是为什么 给我错误的结果在 1 号线?它返回 162.82,应该是 162.83。 相同
  • 请看我对上面帖子的评论,我一直太相信计算器,结果实际上应该是162.82。我的错!
【解决方案2】:

怎么样:

<xsl:value-of select="format-number(Price * Quantity, '0.00')"/>

另请注意,我得到了所有这些相同的 - 预期 - 结果:

<xsl:value-of select="round(Price * Quantity * 100) div 100" />

和:

<xsl:value-of select="round(xs:decimal(Price) * xs:decimal(Quantity) * 100) div 100" />

和:

<xsl:value-of select="round(xs:decimal(Price) * xs:decimal(Quantity) * 10 * 10) div 100"/>

更正:

如果您不想要“银行家四舍五入”,则不能使用 format-number() 函数,因为它与 round-half-to-even() 执行相同的四舍五入 - 请参见此处的第 5 点:http://www.w3.org/TR/xslt20/#formatting-the-number

但是,所有其他方法都按预期四舍五入到最接近的整数。

【讨论】:

  • 对于所有这些变体的所有 4 行,您是否都得到了预期的结果?请问哪个xslt proc。您正在使用?所有这些建议都给了我 162.82 的结果,而不是我在第 1 行所期望的 162.83 的结果。
  • "不是预期的 162.83。" 这是一个没有根据的期望,因为 19 * 8.569736842105263 实际上是 162.824999999999997 - 请参阅 here
  • 嗯...使用普通的老式计算器,我得到 162.825。此外,如果您查看&lt;xsl:value-of select="Price * Quantity"/&gt; 的结果,它是 162.825,应该四舍五入为 162.83。还是??
  • 我花时间用笔和纸进行了非常老式的模拟计算,你是对的@michael.hor257k,结果是162.82499999999997。很抱歉在发布之前没有检查这个!
猜你喜欢
  • 2022-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多