This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 6240 - [FO] round-half-up(value,precision)
Summary: [FO] round-half-up(value,precision)
Status: CLOSED FIXED
Alias: None
Product: XPath / XQuery / XSLT
Classification: Unclassified
Component: Functions and Operators 3.0 (show other bugs)
Version: Working drafts
Hardware: All All
: P2 normal
Target Milestone: ---
Assignee: Michael Kay
QA Contact: Mailing list for public feedback on specs from XSL and XML Query WGs
URL: http://en.wikipedia.org/wiki/IEEE_754...
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2008-11-19 21:42 UTC by Peter Rushforth
Modified: 2009-10-12 22:26 UTC (History)
2 users (show)

See Also:


Attachments

Description Peter Rushforth 2008-11-19 21:42:24 UTC
As an XSLT practitioner, I have had occasion to process data sets which were previously or traditionally processed with legacy office and database software. These software packages provide rounding functionality which by default rounds decimal values which are exactly halfway between the lower and higher numbers to the higher number.  The requirement for XPath was to match the results of legacy systems using the XSLT / XPath languages, such that the results could be compared.  

In fact, the XPath language does provide some rounding functions, but only an incomplete set when compared with the recommended set from the IEEE floating point standard.  

The time it takes to discover errors, correct them, explain why XPath can't do this directly without resort to more complicated methods (extension functions, user-defined functions) represents a barrier to adoption of XPath and hence XSLT and XQuery.

I hope you'll consider adding at least this function from the IEEE standard  in
the next revision of XPath.

Thank you.
Peter Rushforth
Comment 1 Michael Kay 2008-11-25 18:03:43 UTC
There was discussion of this on 25 Nov 2008. Much of the discussion centred on policy questions relating to the general question of how we should respond to requests to add functions to the library; as one might expect, this discussion was inconclusive.

There was some feeling, however, that the proposed function filled a gap somewhere in the space between round() and round-half-to-even(), and the editor was instructed to prepare a proposal - the preferred approach being a variant of round() with an additional argument. In the meantime, however, there was no commitment in principle to adding the function.
Comment 2 Michael Kay 2008-12-01 12:48:52 UTC
Proposal: define a 2-argument version of fn:round, where the second argument is used to indicate the required precision in the same way as fn:round-half-to-even.

The two functions round() and round-half-to-even() should then be the same except for the way that they round values that are half-way between two multiples of 10^-p. Currently round-half-to-even always casts the argument to a decimal; this would be inappropriate for fn:round because it could introduce errors for large doubles that can't be so converted. So the proposal is to state (in line with format-number()) that the conversion to decimal must not lose precision. Proposed text for the two functions is therefore shown below, with differences tagged as appropriate.

<rh2e>
fn:round-half-to-even($arg as numeric?) as numeric?
fn:round-half-to-even($arg as numeric?, $precision as xs:integer) as numeric?
</rhte>
<round>
fn:round($arg as numeric?) as numeric?
fn:round($arg as numeric?, $precision as xs:integer) as numeric?
</round>
Summary: The value returned is the nearest (that is, numerically closest) value to $arg that is a multiple of ten to the power of minus $precision. If two such values are equally near (e.g. if the fractional part in $arg is exactly .500...), the function returns <rh2e>the one whose least significant digit is even</rh2e><round>the one that is closest to positive infinity</round>.

If the type of $arg is one of the four numeric types xs:float, xs:double, xs:decimal or xs:integer the type of the result is the same as the type of $arg. If the type of $arg is a type derived from one of the numeric types, the result is an instance of the base numeric type.

The first signature of this function produces the same result as the second signature with $precision=0.

For arguments of type xs:float and xs:double, if the argument is NaN, positive or negative zero, or positive or negative infinity, then the result is the same as the argument. In other cases, the argument is cast to xs:decimal using an implementation of xs:decimal that imposes no limits on the number of digits that can be represented. The function is applied to this xs:decimal value, and the resulting xs:decimal is cast back to xs:float or xs:double as appropriate to form the function result. If the resulting xs:decimal value is zero, then positive or negative zero is returned according to the sign of the original argument.

If $arg is of type xs:float or xs:double, rounding occurs on the value of the mantissa computed with exponent = 0.

For detailed type semantics, see Section 7.2.3 The fn:abs, fn:ceiling, fn:floor, fn:round, and fn:round-half-to-even functionsFS

Note:

This function is typically used with a non-zero $precision in financial applications where the argument is of type xs:decimal. For arguments of type xs:float and xs:double the results may be counterintuitive. For example, consider <rh2e>round-half-to-even(xs:float(150.0150), 2)</rh2e><round>[some similar example]</round>.

<rh2e>The implementation will convert the argument to the xs:decimal 150.014999389... which will then be rounded to the xs:decimal 150.01 which will be converted back to the xs:float whose exact value is 150.0099945068... whereas round-half-to-even(xs:decimal(150.0150), 2) will result in the xs:decimal whose exact value is 150.02.</rh2e><round>[some similar example]</round>

Note:
fn:round() and fn:round-half-to-even() produce the same result in all cases except when the argument is exactly midway between two multiples of 10 to the power of -$precision.

<round>Other ways of rounding midway values can be achieved as follows. Towards negative infinity: -fn:round(-$x)). Away from zero: fn:round(fn:abs($x))*fn:compare($x,0). Towards zero: fn:abs(fn:round(-$x))*-fn:compare($x,0)</round>
Comment 3 Michael Kay 2008-12-16 22:16:17 UTC
This proposal was accepted at the WG meeting on 16 Dec 2008
Comment 4 Michael Kay 2008-12-16 23:03:25 UTC
The changes have been applied to the master source.

Peter, I would be grateful if you (as the originator) would close the bug to indicate your acceptance of the proposed solution.

Michael Kay 
Comment 5 Peter Rushforth 2008-12-17 12:47:18 UTC
This solution is accepted with thanks!

Peter
Comment 6 Michael Kay 2009-10-12 22:26:45 UTC
Since the new function is agreed and in the current editor's draft, I am marking this closed.