[XSL-LIST Mailing List Archive Home]
[By Thread]
[By Date]
[Recent Entries]
[Reply To This Message]
Re: format-number/bankers' rounding problem
Subject: Re: format-number/bankers' rounding problem
From: Michael Kay <mike@xxxxxxxxxxxx>
Date: Wed, 13 Jul 2011 18:02:54 +0100
|
In XSLT 2.0, round-half-to-even() does what you want.
In XSLT 1.0, format-number() might do what you want, but the
specification is rather vague and you can't rely on portable rounding
behaviour. So you'll have to code it yourself.
The reason
round('0.145' * 100) div 100
doesn't do what you want is that it's using floating-point arithmetic
rather than decimal arithmetic. Decimal arithmetic only becomes
available in XSLT 2.0. If you care about banker's rounding, then you
probably care enough that floating-point isn't really an option for you.
You should hold money values as an integer number of pence.
Michael Kay
Saxonica
On 13/07/2011 17:28, daniel whitney wrote:
I saw the round solution on the web, details below...
Java v. 1.6.0_26-b03
Saxon v. 9.1.0.8J
XSL version="1.0"
I hit the problem of bankers' rounding when performing a transform
using Java and Saxon and I think I have a solution that works, but I
wanted to see if my logic is questionable.
Some background:
To do transforms we use Java, Microsoft and PHP based processors. When
using format-number('0.165', '0.00') the output returned is 0.16
(Java), 0.17 (MS) and 0.17 (PHP). I looked on the web for a solution
to this and saw the round solution. I tried it on a small sample of
data and it looked OK but then noticed something strange. With this:
<xsl:value-of select="format-number(round('0.145' * 100) div 100,
'0.00')"/>, using Saxon, the result is 0.14. Other numbers like 0.165
(0.17), or even trying this: format-number(round('0.0145' * 1000) div
1000, '0.000') (0.015), worked.
This also presented another problem. We use an XSL called template to
format most of our numbers. Parameters it accepts are, of course, the
number, formatting for positive numbers, formatting for negative
numbers, minimum number of decimals and maximum number of decimals. If
I were to use the round number, I would have to write something to
deal with whether to use 100, 1000, 10000 etc to round. Not a huge
deal but .... The clincher though was when I tested the rounding on
negative numbers:<xsl:value-of select="format-number(round('-0.165' *
100) div 100, '(0.00)')"/> (using Saxon) produces "(0.16)". So that
was the nail in the coffin for round.
So what I came up with was just to append '0000001' to the original
decimal (there's already a separate formatting condition for values
with no decimal) and then to format that:<xsl:value-of
select="format-number(concat($numberValueParam,
'0000001'),$formatNumberVar)"/>
So if $numberValueParam = '0.145' and $formatNumberVar is
'0.00;(0.00)' the value I'm formatting is '0.1450000001' and produces
the output 0.15 and works for both positive and negative values.
I concatenate so many 0's as the maximum number of decimals we might
display is 6 and I wanted to ensure that '0.2' would output as
'0.200000' and not '0.200001'.
This solution is simple for me to implement, so, is there a flaw in my
logic? Better, does something already exist where I can just indicate,
round up for positive and down for negative values?
Thanks for any input,
Dan.
|
PURCHASE STYLUS STUDIO ONLINE TODAY!
Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!
Download The World's Best XML IDE!
Accelerate XML development with our award-winning XML IDE - Download a free trial today!
Subscribe in XML format
RSS 2.0 |
|
Atom 0.3 |
|
|