Recipe3.2.Rounding Numbers to a Specified Precision


Recipe 3.2. Rounding Numbers to a Specified Precision

Problem

You want to round to a specific number of decimal places; however, XSLT's round, ceiling, and floor functions always map numbers to integer values.

Solution

XSLT 1.0

Multiply, round, and divide using a power of ten that determines how many decimal digits are required. Assuming $pi = 3.1415926535897932:

<xsl:value-of select="round($pi * 10000) div 10000"/>

results in 3.1416. Similarily:

<xsl:value-of select="ceiling($pi * 10000) div 10000"/>

results in 3.1416, and:

<xsl:value-of select="floor($pi * 10000) div 10000"/>

results in 3.1415.

Rounding to a specific number of decimal places is also achieved using format-number():

<xsl:value-of select="format-number($pi,'#.####')"/>

This results in 3.1416. This will work even if more than one significant digit is in the whole part because format-number never uses a format specification as an indication to remove significant digits from the whole part:

<xsl:value-of select="format-number($pi * 100,'#.####')"/>

This results in 314.1593.

You can use format-number to get the effect of truncating rather than rounding by using one more formatting digit than required and then chopping off the last character:

<xsl:variable name="pi-to-5-sig" select="format-number($pi,'#.#####')"/> <xsl:value-of select="substring($pi-to-5-sig,1,string-length($pi-to-5-sig) -1)"/>

This results in 3.1415.

XSLT 2.0

The new XPath 2.0 round-half-to-even( ) function will do the trick for most applications. The half to even rule means that when the value being rounded is smack between the lower and higher values, then rounding will be in the direction that produces an even result. So round-half-to-even(1.115,2) eq 1.12 and round-half-to-even(1.125,2) eq 1.12 also! In the first case, we round up because 2 is even, and the second rounds down because 3 is not. The theory behind this is that if you have a bunch of numbers, you should roughly round up as often as you round down so rounding errors cancel out. As you probably guessed, the second argument is the number of decimal places to round to.

If your application mandates that 5 in the last decimal place always rounds upward, you can use the techniques employed in the XSLT 1.0 recipe.

Discussion

The multiply, round, and divide technique works well as long as the numbers involved remain within the representational limits of IEEE floating point. If you try to capture too many places after the decimal, then the rules of IEEE floating point will interfere with the expected result. For example, trying to pick up 16 decimal digits of pi will give you only 15:

<xsl:value-of select="round($pi * 10000000000000000) div 10000000000000000"/>

This results in 3.141592653589793, not 3.1415926535897932.

An alternative technique manipulates the number as a string and truncates:

<xsl:value-of select="concat(substring-before($pi,'.'),                        '.',                         substring(substring-after($pi,'.'),1,4))"/>

and results in 3.1415.

The effect of ceiling or round can be obtained by this technique at the cost of additional complexity:

<xsl:variable name="whole" select="substring-before($pi,'.')"/> <xsl:variable name="frac" select="substring-after($pi,'.')"/> <xsl:value-of select="concat($whole,                        '.',                         substring($frac,1,3),                     round(substring($frac,4,2) div 10))"/>

This results in 3.1416.




XSLT Cookbook
XSLT Cookbook: Solutions and Examples for XML and XSLT Developers, 2nd Edition
ISBN: 0596009747
EAN: 2147483647
Year: 2003
Pages: 208
Authors: Sal Mangano

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net