XPath - результат Java XPath неожиданный

Я заметил что-то странное. Это мой XML

<Items>
    <Item>
        <Name>A</Name>
        <Amount>0.0012</Amount>
        <Quantity>17</Quantity>
        <TotalAmount>0.0204</TotalAmount>
    </Item>
    <Item>
        <Name>B</Name>
        <Amount>1</Amount>
        <Quantity>2</Quantity>
        <TotalAmount>2</TotalAmount>
    </Item>
    <Item>
        <Name>C</Name>
        <Amount>3</Amount>
        <Quantity>2</Quantity>
        <TotalAmount>6</TotalAmount>
    </Item>
</Items>

И это XPath, который я использовал

/ Предметы / Предмет [((Количество * Количество)!= Общая сумма)]/ Имя

Этот XPath должен был напечатать название предмета, чей TotalAmount!= Product(Сумма, Количество).

Я получаю значение A. Но я не понимаю, почему это происходит, потому что 0,0012 * 17 = 0,0204

И если я удалю пункт "А", то я не получу результат.

То же самое относится и к более новым версиям XPath.

за $x в /Items/Item[((Количество * Количество)!= TotalAmount)] вернуть $x/ Имя

Я использую Saxon 9 в Java.

Может кто-нибудь объяснить, почему это происходит.

1 ответ

Попробуйте использовать xs:decimal / xs:integer для лучшей точности: /Items/Item[((xs:decimal(Amount) * xs:integer(Quantity)) != TotalAmount)]/Name,

Если вы посмотрите на http://xsltransform.net/94AbWBV который делает

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs">

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>

    <xsl:template match="/">
      <results>
          <names-precise>
            <xsl:value-of select="/Items/Item[((xs:decimal(Amount) * xs:integer(Quantity)) != TotalAmount)]/Name"/>
          </names-precise>
          <names-imprecise>
              <xsl:value-of select="/Items/Item[((Amount * Quantity) != TotalAmount)]/Name"/>
          </names-imprecise>
          <xsl:apply-templates/>
      </results>

    </xsl:template>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Item">
        <xsl:copy>
            <xsl:apply-templates/>
            <double-computation>
                <xsl:value-of select="Amount * Quantity"/>
            </double-computation>
            <decimal-computation>
                <xsl:value-of select="xs:decimal(Amount) * xs:integer(Quantity)"/>
            </decimal-computation>
        </xsl:copy>
    </xsl:template>
</xsl:transform>

Вы можете видеть, что используемой арифметики с плавающей точкой по умолчанию недостаточно для вычисления точного результата:

<results>
   <names-precise/>
   <names-imprecise>A</names-imprecise>
   <Items>
      <Item>
         <Name>A</Name>
         <Amount>0.0012</Amount>
         <Quantity>17</Quantity>
         <TotalAmount>0.0204</TotalAmount>
         <double-computation>0.020399999999999998</double-computation>
         <decimal-computation>0.0204</decimal-computation>
      </Item>
      <Item>
         <Name>B</Name>
         <Amount>1</Amount>
         <Quantity>2</Quantity>
         <TotalAmount>2</TotalAmount>
         <double-computation>2</double-computation>
         <decimal-computation>2</decimal-computation>
      </Item>
      <Item>
         <Name>C</Name>
         <Amount>3</Amount>
         <Quantity>2</Quantity>
         <TotalAmount>6</TotalAmount>
         <double-computation>6</double-computation>
         <decimal-computation>6</decimal-computation>
      </Item>
   </Items>
</results>

Та же самая неточность существует в другом языке, таком как Javascript, использующем формат двойных чисел IEEE:

document.getElementById('result').textContent = 0.0012 * 17;
<p>Result of <code>0.0012 * 17</code> with Javascript is <code id="result"></code>.</p>

Другие вопросы по тегам