xsl-list
[Top] [All Lists]

Re: [xsl] format-number/bankers' rounding problem

2011-07-13 12:03:09

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.

--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail:<mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--




--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--

<Prev in Thread] Current Thread [Next in Thread>