Fix numeric FORMAT ~F and ~E ANSI test failures#744
Open
blakemcbride wants to merge 1 commit intoarmedbear:masterfrom
Open
Fix numeric FORMAT ~F and ~E ANSI test failures#744blakemcbride wants to merge 1 commit intoarmedbear:masterfrom
blakemcbride wants to merge 1 commit intoarmedbear:masterfrom
Conversation
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.
Fix numeric FORMAT ~F and ~E ANSI test failures
Summary
Repairs the
~F(fixed-format) and~E(exponential) directiveimplementations in
format.lisp. Fourteen ANSI conformance failuresin this area now pass, and no previously-passing test regresses.
FORMAT.F.5~FFORMAT.F.8~FFORMAT.F.45~FFORMAT.F.46~FFORMAT.F.46B~FFORMAT.F.47~FFORMATTER.F.45~FFORMATTER.F.46~FFORMATTER.F.46B~FFORMATTER.F.47~FFORMAT.E.1~EFORMAT.E.2~EFORMAT.E.3~EFORMAT.E.26~ERoot causes
The old implementation computed the scale exponent and rounded digits
using
long-floatarithmetic viadecode-float/log/expt.That path:
2.9085037515399494d185,(* exponent (log 2l0 10))plus thesubsequent scale-by-
(expt 10 …)dropped low-order significantdigits; the rendered mantissa no longer round-tripped.
and ~E outputs disagreed with
prin1on boundary cases such as0.999…→1.000, and on the corresponding carry-out of theexponent.
the formatted value was a bare
"."or when width pressure forceda choice between the leading
0(making0.xxxa float token) andthe trailing
0(makingxxx.0a float token). Tests likeFORMAT.F.45/.46/.47require preferring the trailing zero whenboth exist but only one fits, since
.0reads as a float while0.reads as an integer.eexponent marker even when the float'stype matched
*read-default-float-format*. ABCL'sprin1emitsuppercase
Ein that case (inherited from Java'sDouble.toString), soFORMAT.E.1/FORMAT.E.2— which compare(format nil "~e" x)againstprin1-to-stringwithstring=—failed on every in-range sample.
Changes
1.
scale-exponentrewritten with exact rational arithmeticThe new version computes the base-10 exponent from the bit-lengths
of
(rational x)'s numerator and denominator, then refines up ordown with integer
expt. The returned mantissa is exact:No transcendental calls remain in the hot path; extreme doubles and
denormals are handled uniformly.
2. New helper
shortest-digits-and-exponentFor the
d = NILbranch of~E(where CLHS 22.3.3.2 requires theshortest round-trip fraction), we take the trimmed digits and the
implied exponent directly from
sys::float-string(which alreadyproduces Java's shortest round-trip representation). This eliminates
the precision loss that went through
scale-exponent+ rounding.3. New helper
exact-round-digitsFor the
d-given branch, rounding is done on the exact rational:The carry-out branch is what makes
(format nil "~,1,,0e" 9.99d0)produce
"0.1e+2"instead of"0.10e+1".4.
format-exp-auxrewritten to use the helpersThe new body:
dandk(n-sig = 1+dfork>0,d+kotherwise) and delegates toshortest-digits-and-exponentor
exact-round-digits.factor
k:k > 0: split digits at positionkk = 0:".DDD"with a leading-zero flagk < 0:"." + |k| zeros + digitswith a leading-zero flaglpoint/tpointflags and reconciles them against widthin the same style as the old code, but now consistent with the
~Ffix in point 5 below.5.
flonum-to-string+format-fixed-auxtrailing-zero handlingTwo coupled fixes to the ~F path:
flonum-to-string(around line 234): when shortening an over-widedigit string and the integer part is
"0", the caller will prependa leading
"0"that consumes one column ofwidth. Computeeffective-width = (max (1- width) 0)before deciding how manytrailing zeros to drop. Without this, ~F width calculations
over-estimated the space available and produced invalid tokens
(e.g.
"0.") for values like0.05at width 4.format-fixed-aux(around line 2264): rewritten lpoint/tpointreconciliation. When both flags are set and only one column
remains, prefer
tpoint(yielding.5-style output) overlpoint(which would yield
5.-style, an integer token). When only oneflag is set, keep it unless dropping would leave a bare
".".6.
format-exponent-markerdefault case uppercasedformat-exponent-markerreturns the marker character when thefloat's type matches
*read-default-float-format*. Changed from#\eto#\Eso it matchesprin1's output (which inheritsuppercase
EfromString.valueOf(double)). The explicit-typemarkers (
#\f,#\d,#\s,#\l) stay lowercase, which alsomatches
prin1.Files changed
src/org/armedbear/lisp/format.lisp— the only file touched.Test plan
ant abclbuilds cleanly.(
asdf:test-system :abcl/test/ansi/compiled) goes from52 → 50 unexpected failures; the two retired failures are
FORMAT.E.1andFORMAT.E.2. NoFORMAT.F.*,FORMATTER.F.*,FORMAT.E.*tests remain on the failure list.FORMAT.F.3–F.44,FORMAT.E.4–E.25,FORMAT.E.27–E.29, andFORMATTER.F.3–F.44continue topass with mixed-case
string-equalcomparisons.-
(format nil "~e" 2.9085037515399494d185)→"2.9085037515399494E+185"(matchesprin1).-
(format nil "~,1,,0e" 9.99d0)→"0.1E+2"(carry-out).-
(format nil "~,2,,-1e" 5.0)→"0.05e-1"(k<0 layout).Compatibility
No public API change.
FORMATandFORMATTERbecome stricter inthe directions CLHS requires:
~Eoutput is now rounded exactly, so previously-incorrectmantissas on large-magnitude doubles will change (toward the
round-trip-correct form).
~Edefault exponent marker is now uppercaseEfordefault-type floats, matching
prin1. Callers that relied on theprevious lowercase
efor those floats will see a case change inoutput.
~Foutput at tight widths now prefers trailing-zero tokens(
.5) over leading-zero tokens (5.) when forced to choose,matching CLHS's requirement that the result remain readable as a
float.