fix: Specify how a numeric string's leading and trailing zeros render - #180
Open
leongdl wants to merge 1 commit into
Open
fix: Specify how a numeric string's leading and trailing zeros render#180leongdl wants to merge 1 commit into
leongdl wants to merge 1 commit into
Conversation
seant-aws
approved these changes
Sep 1, 2026
Contributor
Author
|
Pending fixing openjd py and rs. |
`<intstring>` and `<floatstring>` are defined only as "a string whose value is the string representation of" a number, which says what such an element denotes but not what it renders as. Implementations read it both ways, and both readings were already pinned by landed conformance fixtures pulling in opposite directions: base/jobs/2.3--int-param-intstring-default-resolves asserts COUNT:7 from an INT default of '007' -- the text is not preserved. EXPR/jobs/expr1.3.4--float-passthrough asserts PARAM:3.500 from a FLOAT default of "3.500" -- the text is preserved. Both are right, because the two kinds of zero are not the same thing. Add §7.5 Numeric strings to say so, and cross-reference it from the four places `<intstring>`/`<floatstring>` are defined. Leading zeros are redundant to the value and are removed: '02' and '2' ask for the same thing, and forwarding the text invokes a renderer with `--frame 02`. The decimal places a <floatstring> is written with are preserved: '2.50' renders 2.50, because a fixed number of decimal places is a thing renderers require and the string form is the only way a Template can ask for one -- a <float> literal cannot, since 2.50 and 2.5 are the same literal after parsing. Exponent notation and an explicit leading '+' are called out as unspecified in this revision rather than left to be inferred. Implementations disagree on both and no fixture pins either: measured across two implementations and two surfaces, '1E+2' renders as 1E+2 in three cells and 100.0 in one, and '+2.50' renders as +2.50 in two and 2.50 in two. The new fixture pins both rules on the range-element surface, for INT and FLOAT. '0.50' is included because its leading zero is not redundant -- it is the whole integer part -- so a text-based strip must still leave one digit behind. The fixture brackets each value (`W[2.50]`) instead of delimiting it with a colon. The runner matches expected output as a substring, so an assertion of `W:2.5` is satisfied by a line reading `W:2.50`; the closing bracket is what makes the `forbidden` entries forbid anything. base/jobs/3.4.1.2 in OpenJobDescription#179 has exactly that defect and reports a pass against either behaviour. Verification. The fixture passes against openjd-model-for-python#345 and fails against openjd-rs at 1b58c03, which renders W[2.5], W[3.5] and W[0.5] -- so it discriminates rather than just describing one implementation. Both CI conformance jobs install released versions (`pip install openjd-cli`, `cargo install openjd-cli`) and will fail this fixture until both implementations ship the fix; released Python 0.11.6 fails it on the INT side too. Zero-valued <floatstring>s ('0.00') are left out: openjd-rs collapses the value to 0.0 and loses the written scale, which is a separate pre-existing defect that also affects FLOAT parameter defaults. Signed-off-by: David Leong <116610336+leongdl@users.noreply.github.com>
leongdl
force-pushed
the
fix/numeric-string-zero-handling
branch
from
September 4, 2026 22:30
ae3f433 to
6dce253
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds §7.5 Numeric strings, which says what an
<intstring>/<floatstring>renders as, and one conformance fixture that pins it for range elements.The rule, in one line: leading zeros go, trailing zeros stay.
Why the spec needs to say this
<intstring>and<floatstring>are defined only as "a string whose value is the string representation of" a number. That says what the element denotes; it does not say what it renders as. Implementations read it both ways, and two already-landed fixtures pull in opposite directions:base/jobs/2.3--int-param-intstring-default-resolvesCOUNT:7from INTdefault: '007'EXPR/jobs/expr1.3.4--float-passthroughPARAM:3.500from FLOATdefault: "3.500"Both are right, and the reason is that the two kinds of zero are not the same thing:
'02'and'2'ask for the same thing, and forwarding the text invokes a renderer with--frame 02.<float>literal cannot, because2.50and2.5are the same literal after parsing.This came out of review on openjd-model-for-python#342, which normalized range elements to the number they denote and so dropped the trailing zeros. openjd-model-for-python#345 reverts that half; this PR is the spec statement behind it.
base/jobs/3.4.1.2--float-range-floatstring-elements-normalizedin #179 states the fully-normalizing reading for a FLOAT range element. Its own comment flagged that it andexpr1.3.4--float-passthroughcould not both be right and that a ruling was needed. This is the ruling, and 3.4.1.2 is the one that moves — see the note on #179 below.What is deliberately left unspecified
Exponent notation and an explicit leading
+are called out in §7.5 as unspecified in this revision, rather than left for a reader to infer. Measured across both implementations and two surfaces (job parameter default, task parameter range element), after the fixes in openjd-model-for-python#345 and openjd-rs#354:'1E+2'1E+21E+21E+21E+2'1e-3'0.0011e-31e-31e-3'+2.50'2.50+2.50+2.50+2.50The range surface now agrees; the default surface does not, and nothing pins it. So §7.5 says so and tells a Template author to write the value they want.
The fixture
base/jobs/7.5--numeric-string-zeros-in-range-elements.test.yaml, covering INT and FLOAT range elements in one template.'0.50'is in there because its leading zero is not redundant — it is the whole integer part — so an implementation that strips leading zeros by text has to leave one digit behind.One detail worth flagging for reviewers: the fixture brackets each value (
W[2.50]) rather than delimiting with a colon. The runner matches expected output as a substring, so an assertion ofW:2.5is satisfied by a line readingW:2.50. The closing bracket is what makes theforbiddenentries forbid anything.base/jobs/3.4.1.2in #179 has exactly that defect and reports a pass against either behaviour — that is how the contradiction went unnoticed.Verification
The fixture discriminates. Full
2023-09/*suite (1162 fixtures) run against both implementations, each from source, before and after their matching fix:e7a17b3→ #345)9a9998d→ #354)In both baselines the single failure is this fixture and nothing else, so it pins the new rule without disturbing any existing assertion. The Python runs used
openjd-cliat mainline7c7ece4withopenjd-sessions0.10.14, the newest combination that CLI supports.Both conformance workflows install released versions —
pip install openjd-cliandcargo install openjd-cli— and neither release has the fix:f64, so it fails on the trailing-zero side.That is what CI now shows, and it is worth reading because the two implementations fail differently:
1161 passed, 1 failedW[3.500]1161 / 1116 / 1161 passed, 1 failedW[2.50]In every job the only failure is this fixture, it parsed cleanly, and the job ran — the diagnostic is a missing expected line, not a syntax or schema error. Released Python drops the leading zero from
'03.500'and so missesW[3.500]; released Rust normalizes'2.50'to2.5and so missesW[2.50]. The fixture discriminates in both directions.One unrelated failure appears in the Python job on Windows,
3.4--path-parameter. It fails onmainlinetoo — that workflow has been red on Windows since at least 2026-07-15 — so it is a pre-existing baseline failure, not a regression from this PR.Verified passing against both fixes built from source, as above. Happy to split the fixture into a follow-up PR if the convention is to keep the spec change green, in the manner of #162–#166.
Also left out
Negative zero written with decimal places (
'-0.00') is not asserted. Both implementations render zero's sign away, but they differ on whether the decimal places survive it, and §7.5 has nothing to say about a sign on zero.Related
Float64::with_strguard that was dropping the decimal places of any zero.3.4.1.1and3.4.1.2.3.4.1.1(INT) agrees with §7.5 as written.3.4.1.2assertsW:2.5from'02.50'and needs to becomeW:2.50, with a terminator so substring matching cannot hide it. Flagging rather than editing that branch from here.