web-dev-qa-db-fra.com

XSL - problème d'arrondi/numéro de format

J'essaie d'obtenir la valeur d'un nombre à 2 déc places à partir de mon xml.

XML: <Quantity>0.0050</Quantity>

XSL: <xsl:value-of select="format-number($quantity, '####0.00')" />

Cependant, XSL semble avoir un problème avec cette valeur et génère 0.00 dans une zone de la page et 0.01 dans l'autre. Bien sûr, dans cette situation, il est avantageux d’avoir une sortie 0.01 dans tous les domaines.

Une autre zone a la valeur 4.221 et pourtant le XSL affiche 4.23.

Je me rends bien compte que format-number convertit un nombre en chaîne. 

Vous ne savez pas comment résoudre ce problème.


MODIFIER:

Ok, après un peu de bricolage, j'ai trouvé que ça marche:

<xsl:value-of select='format-number( round(100*$quantity) div 100 ,"##0.00" )' />

Via ce site web

Comme ce type le mentionne, XSL utilise les mots «les banquiers arrondis» pour arrondir les nombres pairs au lieu des plus grands.

La solution semble à peine élégante et consiste à ajouter une tonne de fonctions supplémentaires à un fichier XSL déjà volumineux et complexe. Il me manque sûrement quelque chose?

24
Julio

J'ai eu d'innombrables problèmes avec les nombres décimaux dans XSLT/XPath 1.0, souvent une combinaison représentant les nombres décimaux sous forme de nombres à virgule flottante et arrondis à moitié égal (arrondi bancaire). Malheureusement, l'approche round(yournum*100) div 100 n'a pas fonctionné pour moi en raison de l'imprécision en virgule flottante. Par exemple, en multipliant 1,255 par 100, vous obtenez 125.4999999999999999 (ceci n'est pas censé dépendre de l'implémentation, car il est supposé être conforme à IEEE 754, mais je ne sais pas si toutes les implémentations y adhèrent). Lorsque arrondi, cela donne alors 125, plutôt que le 126 souhaité.

J'ai adopté l'approche suivante, qui, à mon avis, fonctionne (bien que ce soit toujours un domaine délicat, je ne vais donc pas déclarer trop de confiance!). Toutefois, cela dépend du moteur XSLT prenant en charge les extensions EXSLT. Cela suppose que vous vouliez arrondir à deux décimales.

<func:function name="local:RoundHalfUp">
    <xsl:param name="number"/>

    <xsl:choose>
        <xsl:when test="contains($number, '.')">
            <xsl:variable name="decimal" select="estr:split($number, '.')[2]"/>
            <xsl:choose>
                <xsl:when test="string-length($decimal) &gt; 2">
                    <func:result select="format-number(concat($number, '1'), '0.00')"/>
                </xsl:when>
                <xsl:otherwise>
                    <func:result select="format-number($number, '0.00')"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
            <func:result select="format-number($number, '0.00')"/>
        </xsl:otherwise>
    </xsl:choose>

</func:function>

qui peut s'appeler comme:

<xsl:value-of select="local:RoundHalfUp(1.255)"/>

Les espaces de noms sont:

xmlns:func="http://exslt.org/functions"
xmlns:estr="http://exslt.org/strings"
xmlns:local="http://www.acme.org/local_function"

Il est important de noter que la fonction ajoute a '1', pas 0,001 ou similaire.

Il est certainement préférable d’utiliser XSLT 2.0 s’il s’agit d’une option (car le type décimal est correct), mais je sais que ce n’est souvent pas une option (par expérience douloureuse!).

14
Giles

J'ai aussi rencontré le problème suivant: si vous le faisiez. round ($ number * 100) div 100vous obtenez une représentation en virgule flottante de .4999999 et l'arrondi ne fonctionne pas correctement.

Pour contourner cela, cela semble fonctionner: round ($ number * 1000 div 10) div 100

0
Sean

Par rapport à toutes les réponses ci-dessus, j'ai trouvé

<xsl:value-of select="format-number(xs:decimal($quantity), '####0.00')" />

est utile, il ne tronque aucune valeur décimale. tronquer exactement les nombres spécifiés. si nombre est au format exponentiel, nous pouvons utiliser comme suit:

<xsl:value-of select="format-number(xs:decimal(number($quantity)), '####0.00')" />

Merci

0
Rakesh