
Math and XSLT
by Bob DuCharme
July 05, 2001
A Russian translation of this article can be found here.
XSLT's full support of XPath's math capabilities lets you do all
the basic kinds of arithmetic and a little more. Let's look at a
stylesheet that demonstrates these capabilities by using the values
from this document:
<numbers>
<x>4</x>
<y>3.2</y>
<z>11</z>
</numbers>
Lines A through N of the stylesheet each make (or attempt to make)
a different calculation. These calculations use the numbers in the
document above and some other numbers that are either hardcoded in the
stylesheet or retrieved from functions that return numbers.
<xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:outputmethod="xml"omit-xml-declaration="yes"/>
<xsl:templatematch="numbers">
A.4+3.2=<xsl:value-ofselect="x+y"/>
B.3.2-4=<xsl:value-ofselect="y-x"/>
C.4*3.2=<xsl:value-ofselect="x*y"/>
D.11/3.2=<xsl:value-ofselect="zdivy"/>
E.4+3.2*11=<xsl:value-ofselect="x+y*z"/>
F.(4+3.2)*11=<xsl:value-ofselect="(x+y)*z"/>
G.11mod4=<xsl:value-ofselect="zmodx"/>
H.4+3.2+11=<xsl:value-ofselect="sum(*)"/>
I.floor(3.2)=<xsl:value-ofselect="floor(y)"/>
J.ceiling(3.2)=<xsl:value-ofselect="ceiling(y)"/>
K.round(3.2)=<xsl:value-ofselect="round(y)"/>
L.11+count(*)=<xsl:value-ofselect="11+count(*)"/>
M.3.2+string-length("3.2")=
<xsl:value-ofselect="y+string-length(y)"/>
N.11+"hello"=<xsl:value-ofselect="z+'hello'"/>
</xsl:template>
</xsl:stylesheet>
Before we talk about what each line is doing, let's look at the
result of applying the stylesheet to the numbers
document.
A.4+3.2=7.2
B.3.2-4=-0.8
C.4*3.2=12.8
D.11/3.2=3.4375
E.4+3.2*11=39.2
F.(4+3.2)*11=79.2
G.11mod4=3
H.4+3.2+11=18.2
I.floor(3.2)=3
J.ceiling(3.2)=4
K.round(3.2)=3
L.11+count(*)=14
M.3.2+string-length("3.2")=
6.2
N.11+"hello"=NaN
The stylesheet has a single template rule for the source tree's
numbers element. This template has a series of
xsl:value-of instructions whose select attributes
use the values of the numbers element's x,
y, and z child elements to do various kinds of
math. Mathematical expressions like these can use the full power of
XPath to say which element or attribute has a number they need; this
stylesheet, however, is more concerned with demonstrating the range of
mathematical operations available than with using fancy XPath
expressions to retrieve elements and attributes from odd parts of a
document.
Line A of the template adds the value of x
(4) to the value of y (3.2) and puts their sum, 7.2, in the
result tree. It's simple, straightforward, and shows that you're not
limited to integers for stylesheet math.
Line B subtracts 4 from 3.2 for a result of -0.8. Negative numbers
shouldn't pose any difficulties for XSLT processors.
Warning With some
XSLT processors, the use of decimal numbers may introduce a tiny
error. For example, the "3.2 - 4" in this example comes out as
"-.7999999999999998" on some processors. While being off by
.0000000000000002 isn't much, being off at all shows that math is not
XSLT's strong point.
Line C multiplies 4 and 3.2, using the asterisk as the
multiplication operator, for an answer of 12.8.
Line D divides the value of the z element, (11), by 3.2,
showing an XSLT processor's ability to perform floating-point
division. Although most programming languages traditionally use the
slash character to represent mathematical division, XPath already uses
the slash to separate the steps in an XPath location path (for
example, wine/vintage to represent the vintage child
element of the wine element). 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. With the parentheses around the "4+3.2", that happens first, so
that (4 + 3.2) * 11 = 7.2 * 11.
Line G demonstrates the mod operator, which shows the
remainder if you divide the first term by the second. The example
shows that 11 mod 4 equals 3, because 4 goes into 11 twice with 3 left
over. This operator is great for checking whether one number divides
into another evenly; just check whether the larger number mod the
smaller equals zero.
Line H demonstrates the sum() function. With a node list
as an argument, it sums all the numbers in the list. In the example
the asterisk means "all the children of the context node" -- the
numbers element's x, y, and z
children.
Lines I and J demonstrate the floor() and
ceiling() functions. If you pass either of these an integer,
they return that integer. If you pass floor() a non-integer,
it returns the highest integer below that number. In the example,
floor(3.2) is 3. The ceiling function returns the
smallest integer above a non-integer number; in the example,
ceiling(3.2) equals 4.
Line K's round() function rounds off a non-integer by
returning the closest integer. When 3.2 is passed to it, it returns 3;
passing 3.5 or 3.6 to it would cause it to return 4.
Line L incorporates another XPath function, count(), which
returns the number of nodes in the set passed to it as an argument.
XPath offers several functions that, while not explicitly
mathematical, return numbers and can be used for any calculations you
like: count(), last(), position(), and
string-length(). Line M demonstrates
string-length(), which returns the length of a string.
Also in Transforming XML
Automating Stylesheet Creation
Appreciating Libxslt
Push, Pull, Next!
Seeking Equality
The Path of Control
Line N demonstrates what happens when you try to perform math with
something that isn't a number: when 11 gets added to the string
"hello", the result is the string "NaN", an abbreviation for "Not a
Number." When you pull a number out of an element's content or
attribute value and then use it for a calculation, you can't always be
sure that what you pulled is really a number, so XSLT's clearly
defined behavior for the unworkable case makes it easier to check for
and cope with in your code.
XSLT is about manipulating text, not numbers, but you can build on
the mathematical operations provided as part of XSLT to perform more
complicated calculations. For example, the following stylesheet, which
accepts any document as input, computes the value of pi. The precision of the result
depends on the value of the iterations variable.
<xsl:stylesheetxmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:outputmethod="text"/>
<!--Computepi.BasedonLeibniz'salgorithmthat
pi/4=1-1/3+1/5-1/7+1/9-1/11...whichIdidas
pi=4-4/3+4/5-4/7+4/9-4/11...
-->
<xsl:variablename="iterations"select="80000"/>
<xsl:templatename="pi">
<!--namedtemplatecalledbymaintemplatebelow-->
<xsl:paramname="i">1</xsl:param>
<xsl:paramname="piValue">0</xsl:param>
<xsl:choose>
<!--Iftherearemoreiterationstodo,addthepassed
valueofpitoanotherroundofcalculations.-->
<xsl:whentest="$i<=$iterations">
<xsl:call-templatename="pi">
<xsl:with-paramname="i"select="$i+4"/>
<xsl:with-paramname="piValue"
select="$piValue+(4div$i)-(4div($i+2))"/>
</xsl:call-template>
</xsl:when>
<!--Ifnomoreiterationstodo,add
computedvaluetoresulttree.-->
<xsl:otherwise>
<xsl:value-ofselect="$piValue"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:templatematch="/">
<xsl:call-templatename="pi"/>
</xsl:template>
</xsl:stylesheet>
The repetition is implemented using a recursive named
template. With the iterations setting shown, the stylesheet
creates this result:
3.1415676535897985
With that many iterations, the answer is only accurate up to the
first four digits after the decimal. Of course, if you want to compute
the value of pi seriously, there are many more appropriate languages,
but it's nice to know that you can push XSLT to do some fairly complex
math when necessary.