fix(xpath): format a large integer-valued number with no decimal point (#268)
XPath 1.0 4.2: an integer-valued number converts to a string with no decimal point. The formatter only took the integer branch for a magnitude below 1e15, so a value like 1234567890123456 (exact in a double, which holds integers exactly to 2^53) printed as 1234567890123456.0. Raise the bound to 2^63, the range where the Int64 conversion is safe; larger magnitudes still expand through the exponent path. Closes Apache Xalan conformance case string132.
This commit is contained in:
parent
9070ccc3f1
commit
95fde5b177
@ -52,6 +52,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- A large integer-valued number now converts to a string with no decimal point (XPath 1.0 4.2). The number-to-string formatter only treated a value below 1e15 as an integer, so an integer such as `1234567890123456` (still exact in a double, which represents integers exactly up to 2^53) printed as `1234567890123456.0`. The integer bound is now 2^63, the range in which the conversion is safe. Closes Apache Xalan conformance case `string132`.
|
||||
|
||||
- A lone `/` (the root node) is now parsed correctly when it is followed by something other than the end of the expression, such as a `)`, `]`, `|`, or an operator. The XPath grammar makes the relative part after `/` optional, but the compiler only treated `/` as the root path at the very end of the expression, so `count(/)`, `generate-id(/)`, and `/ = .` tried to parse a step from the following token and yielded an empty node-set. The compiler now recognizes a `/` as the root whenever no location step follows it. Closes Apache Xalan conformance cases `position106` and `mdocs15`.
|
||||
|
||||
- A template match pattern written with an explicit `child::` or `attribute::` axis now gets the default priority of its node test (XSLT 1.0 5.5), not 0. `attribute::node()` is a `node()` node test, so its default priority is -0.5 (the same as `@*`), but it was treated as a name test and given priority 0, which made it wrongly outrank `@*`. The axis is now stripped before the node test is classified. Closes Apache Xalan conformance cases `conflictres29` and `conflictres30`.
|
||||
|
||||
@ -58,7 +58,11 @@ public extension PureXML.XPath {
|
||||
static func format(_ value: Double) -> String {
|
||||
if value.isNaN { return "NaN" }
|
||||
if value.isInfinite { return value < 0 ? "-Infinity" : "Infinity" }
|
||||
if value == value.rounded(), abs(value) < 1e15 {
|
||||
// An integer-valued double prints with no decimal point. The bound is
|
||||
// 2^63 so the Int64 conversion cannot trap; exact integers run to 2^53
|
||||
// and the representable ones above it are still whole, and a magnitude
|
||||
// beyond 2^63 falls to the exponent expansion below.
|
||||
if value == value.rounded(), abs(value) < 0x1p63 {
|
||||
return String(Int64(value))
|
||||
}
|
||||
return expandExponent(String(value))
|
||||
|
||||
@ -36,5 +36,4 @@ sort/sort27
|
||||
sort/sort32
|
||||
sort/sort33
|
||||
sort/sort34
|
||||
string/string132
|
||||
whitespace/whitespace20
|
||||
|
||||
@ -58,6 +58,15 @@ struct XPathExpressionTests {
|
||||
#expect(try string("0 div 0") == "NaN")
|
||||
}
|
||||
|
||||
@Test("a large integer-valued number prints with no decimal point")
|
||||
func test_largeIntegerFormatting() throws {
|
||||
// XPath 4.2: an integer-valued number has no decimal point, including a
|
||||
// large integer still exact in a double (Apache Xalan string132).
|
||||
#expect(try string("1234567890123456") == "1234567890123456")
|
||||
#expect(try string("-1234567890123456") == "-1234567890123456")
|
||||
#expect(try string("123456789012345 * 10") == "1234567890123450")
|
||||
}
|
||||
|
||||
@Test("Booleans coerce to strings and numbers")
|
||||
func test_booleanCoercion() throws {
|
||||
#expect(try string("1 = 1") == "true")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user