XSLT与javax.xml.transform(0.2 * 0.8 * 0.8)

时间:2016-09-07 16:13:18

标签: java xml xslt xslt-2.0

我有一个 XSLT,如下所示

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>

<xsl:template match="Test">

        <Result>
            <xsl:value-of select="number(depth)*number(width)*number(height)"/>
        </Result>


</xsl:template>

当我针对Altova XML或W3CSchool here中的以下示例文件测试此XSLT时,我得到的结果为0.128

示例文件:

<?xml version="1.0" encoding="UTF-8"?>
<Test>
<depth>.8</depth>
<width>.8</width>
<height>.2</height>
</Test>

但是,当我使用Java调用XSLT时,情况会发生变化。我得到了结果

<Result>0.12800000000000003</Result>

以下是我使用的简单代码:

 import javax.xml.transform.*;
    import javax.xml.transform.stream.StreamResult;
    import javax.xml.transform.stream.StreamSource;
    import java.io.File;
    import java.io.IOException;
    import java.net.URISyntaxException;

public class TestMain {
    public static void main(String[] args) throws IOException, URISyntaxException, TransformerException {
        TransformerFactory factory = TransformerFactory.newInstance();
        Source xslt = new StreamSource(new File("transform.xslt"));
        Transformer transformer = factory.newTransformer(xslt);

        Source text = new StreamSource(new File("input.xml"));
        transformer.transform(text, new StreamResult(new File("output.xml")));
    }
}

问题:为什么Java代码输出为0.12800000000000003? 甚至0.12800000000000000也是可以理解的,但0.12800000000000003是不正确的计算。

1 个答案:

答案 0 :(得分:2)

首先,浮点运算通常会产生这样的舍入误差,因为在xs:double的值空间中无法准确表示0.8之类的数字。

其次,样式表显式使用number()函数,它将源文档中的值(如0.8)转换为浮点,在XSLT 1.0和XSLT 2.0中都是如此。 XSLT 2.0提供了一个解决方案,您可以通过number()上的调用替换对xs:decimal()的调用,这将为您提供十进制算术而不是二进制浮点,从而避免舍入错误。但是你正在执行的代码在两种情况下都在进行浮点运算。

根据W3C规范在1.0和2.0中的规则,该表达式的正确答案实际上是0.12800000000000003。该规范并未对此给予任何宽大处理。但实现者采用快捷方式,并使用库来进行浮点运算(更具体地说,用于数字到字符串的转换),这些库并未按照W3C规则编写。我强烈怀疑为此查询输出0.128的实现正在使用数字到字符串转换例程,该例程试图比W3C规范允许的更聪明。

如果您想避免这种舍入错误,正确的方法是:

(a)使用XSLT 1.0,使用format-number()将输出格式化为可能准确(或实际需要)的小数位数

(b)使用XSLT 2.0,使用xs:decimal算法 - 当您从源文档中读取数字时,意味着通过根据声明类型为xs的模式验证源文档来明确地将它们设置为xs:decimal:十进制,或在样式表中使用xs:decimal()函数。