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>