[xsl] format-number/bankers' rounding problem

Subject: [xsl] format-number/bankers' rounding problem
From: daniel whitney <dbf.whitney@xxxxxxxxx>
Date: Wed, 13 Jul 2011 12:28:42 -0400
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.

Current Thread