2016-09-07 3 views
0

Ich habe eine XSLT wie unten:Falsche Multiplikationsergebnis durch XSLT mit javax.xml.transform (0,2 * 0,8 * 0,8)

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

Wenn ich testen dieses XSLT gegen die Probe unter Datei in Altova XML oder in W3CSchool here, erhalte ich das Ergebnis als 0.128

Beispieldatei:

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

Die Dinge ändern sich jedoch, wenn ich Java verwende, um das XSLT aufzurufen. Ich bekomme das Ergebnis als

<Result>0.12800000000000003</Result> 

Im Folgenden wird der einfache Code Ich verwende:

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"))); 
    } 
} 

Frage: Warum wird der Java-Code die Ausgabe als 0,12800000000000003 geben? Auch 0,12800000000000000 ist verständlich, aber 0,12800000000000003 ist eine falsche Berechnung.

+1

Mögliche Duplikate von [Warum sind Gleitkommazahlen ungenau?] (Http://stackoverflow.com/questions/21895756/why-are-floating-point-numbers-inaccurate) –

+0

Siehe: http://StackOverflow.com/questions/37400663/dezimal-genauigkeit-in-xslt/37407426 # 37407426 –

+0

@ michael.hor257k Verstanden. Aber es ist XSLT V2. Also, Altova und Java-Programm sollte das gleiche Ergebnis geben, wenn wir oben sagen, Java-Code verwendet nicht XSLT V2 aber V1? – nitgeek

Antwort

2

Erstens erzeugt Gleitkommaarithmetik im Allgemeinen solche Rundungsfehler, weil Zahlen wie 0.8 im Wertebereich von xs: double nicht genau dargestellt werden können.

Zweitens verwendet Ihr Stylesheet explizit die number()-Funktion, die Werte im Quelldokument (wie 0.8) in Fließkommazahlen konvertiert, sowohl in XSLT 1.0 als auch in XSLT 2.0. XSLT 2.0 bietet eine Lösung, bei der Sie den Aufruf von number() durch einen Aufruf von xs:decimal() ersetzen können, der Ihnen anstelle eines binären Fließkommas Dezimalarithmetik und damit die Rundungsfehler vermeiden würde. Der Code, den Sie gerade ausführen, führt in beiden Fällen Gleitkommaarithmetik aus.

Die richtige Antwort auf diesen Ausdruck gemäß den Regeln der W3C-Spezifikation in 1.0 und 2.0 ist tatsächlich 0.12800000000000003. Die Spezifikation gibt diesbezüglich keine Nachsicht. Aber Implementierer nehmen Abkürzungen und verwenden Bibliotheken für Fließkomma-Arithmetik (und insbesondere für die Umwandlung von Zahlen in Strings), die nicht nach den W3C-Regeln geschrieben wurden. Ich würde stark vermuten, dass eine Implementierung, die 0,128 für diese Abfrage ausgibt, eine Nummer-zu-String-Konvertierungsroutine verwendet, die versucht, intelligenter zu sein als die W3C-Spezifikation zulässt.

Wenn Sie diese Art von Rundungsfehler vermeiden wollen, der richtige Ansatz ist:

(a) mit XSLT 1.0, verwenden Sie Format-Nummer(), um die Ausgabe an die Anzahl der Dezimalstellen zu formatieren, die wahrscheinlich um genau zu sein (oder die tatsächlich benötigt werden)

(b) mit XSLT 2.0, verwenden xs: Dezimalarithmetik - das, wenn Sie Zahlen aus dem Quelldokument lesen bedeutet, dass sie explizit xs: decimal, entweder durch die Überprüfung der Quelle Dokumentieren Sie gegen ein Schema, das den Typ als xs: decimal deklariert, oder indem Sie die Funktion xs: decimal() im Stylesheet verwenden.

Verwandte Themen