Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSL - rounding/format-number problem

I'm trying to get the value of a number to 2 dec places from my xml.

XML:<Quantity>0.0050</Quantity>

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

However XSL seems to have a problem with this value and outputs 0.00 in one area of the page and 0.01 in the other. Of course in this situation it is favourable to have 0.01 output in all areas.

Another area has the value 4.221 yet the XSL is outputting 4.23.

I do realise that format-number as a method converts a number to a string.

Not sure how to fix this.


EDIT:

Ok after a bit of mucking around i found that this works:

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

Via this website

As this guy mentions XSL uses 'bankers rounding' to round to even numbers instead of the bigger ones.

The solution hardly seems elegant, and means adding a ton of extra functions to an already bulky and complicated XSL file. Surely i'm missing something?

like image 540
Julio Avatar asked Sep 27 '10 15:09

Julio


People also ask

How do you round off numbers in XSLT?

You can round to the nearest integer (round), always round up (ceiling) (), or always round down (floor). Figure 4.13. In this XML excerpt, you can see that the image's size is 528 pixels wide by 349 pixels tall. x s l t ...

What is Number () in XSLT?

Specifies the format pattern. Here are some of the characters used in the formatting pattern: 0 (Digit)

How do I format XSLT?

The format date on XSLT Technique uses a template dealing with Gregorian Time and Internationalization. The parameter for this date function is a picture string that encloses code in square brackets(D[07]). The name attributes declare date format or default date. It should be declared only once.

How do you do division in XSLT?

Accordingly, XPath and XSLT use the string "div" for division. Lines E and F show how parentheses have the same effect on operator precedence that they have in normal math notation: without them, multiplication happens before addition, so that 4 + 3.2 * 11 = 4 + 35.2.


2 Answers

Not sure why format would be so inconsistent but from memory the spec for it is...complex.

Meantime, you can use the round function (ref). Which is less than perfect, but is functional. If you need to have a particular number of sig figs you can use THE POWER OF MATHS! and do something like:

<xsl:value-of select="round(yournum*100) div 100"/>

like image 60
annakata Avatar answered Sep 16 '22 14:09

annakata


I've had endless trouble with decimals in XSLT/XPath 1.0, often a combination of it representing decimals as floating-point numbers and it using half-even rounding (banker's rounding). Unfortunately, the round(yournum*100) div 100 approach didn't work for me, due to floating-point imprecision. For example, multiplying 1.255 by 100 gives 125.49999999999999 (this isn't meant to be implementation dependent, as it's supposed to be IEEE 754, but I don't know if all implementations do adhere to that). When rounded, this then gives 125, rather than the desired 126.

I've taken the following approach, which I think works (although this is always a tricky area so I won't declare too much confidence!). However, it depends on your XSLT engine supporting EXSLT extensions. It presumes you want to round to two decimal places.

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

which can be called like:

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

The namespaces are:

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

It's important to note that the function appends a '1', not adds 0.001 or similar.

Definately better to use XSLT 2.0 if it's an option (because it has a proper decimal type), but I know that's often not an option (from painful experience!).

like image 25
Giles Avatar answered Sep 17 '22 14:09

Giles