Skip to content

Fix value-kind mismatch in ffc_negative_digit_comp (float slow path)#4

Open
fcostaoliveira wants to merge 1 commit into
mainfrom
fix/negative-digit-comp-vk-clean
Open

Fix value-kind mismatch in ffc_negative_digit_comp (float slow path)#4
fcostaoliveira wants to merge 1 commit into
mainfrom
fix/negative-digit-comp-vk-clean

Conversation

@fcostaoliveira
Copy link
Copy Markdown

Summary

ffc_negative_digit_comp (the negative-exponent big-integer slow path) built its rounded-down reference value b with a hardcoded FFC_VALUE_KIND_DOUBLE, then read it back with the caller's kind:

ffc_am_to_float(false, am_b, &b, FFC_VALUE_KIND_DOUBLE);        // writes b.d (8 bytes)
ffc_adjusted_mantissa theor = ffc_to_extended_halfway(b, vk);  // reads b.f (4 bytes) when vk==FLOAT

For vk == FFC_VALUE_KIND_FLOAT this writes the 8-byte double member of the ffc_value union and reads back the 4-byte float member, so theor is garbage and the big-integer tie-break can round the wrong way. The fix is one token: pass the caller's vk.

Impact

Latent for normal inputs (the shortest-form exhaustive sweep never reaches this path). But real for over-precise, hard-to-round float inputs with a negative exponent: a sweep of binary32 midpoints found ~1.07e9 inputs that round to the wrong neighbor under the bug, e.g.

1.1755301539980510070448986385130987351758e-38
  strtof -> 1.17553008e-38 (0x008000ff)   correct
  ffc    -> 1.17553022e-38 (0x00800100)   before this fix (off by 1 ULP)

Tests

  • Four midpoint cases added to test_src/float_cases.csv (the harness compares ffc_from_chars<float> vs strtof bit-for-bit). They fail before this change ("Test failures from csvs: 4"), pass after.
  • Full float exhaustive (test_exhaustive, all 2^32 values) is "all ok" with the fix; the strtof-parity midpoint sweep drops from ~1.07e9 mismatches to 0. make test passes.

ffc_negative_digit_comp built the rounded-down reference value `b` with a
hardcoded FFC_VALUE_KIND_DOUBLE, then read it back via
ffc_to_extended_halfway(b, vk). For vk == FFC_VALUE_KIND_FLOAT this writes the
8-byte double member of the ffc_value union and reads back the 4-byte float
member, so `theor` is computed from garbage and the big-integer tie-break can
round the wrong way. Pass the caller's vk consistently.

Latent for normal inputs (the shortest-form exhaustive sweep never reaches this
path), but real for over-precise hard-to-round float inputs with a negative
exponent: ~1.07e9 binary32 midpoints round to the wrong neighbor under the bug,
e.g. "1.1755301539980510070448986385130987351758e-38" -> 0x00800100 instead of
the correct 0x008000ff (per strtof).

Adds four such midpoint cases to float_cases.csv (fail before, pass after); the
full float exhaustive suite is "all ok" with the fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant