Skip to content

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

Merged
kolemannix merged 1 commit into
kolemannix:mainfrom
redis-performance:fix/negative-digit-comp-vk-upstream
Jun 15, 2026
Merged

Fix value-kind mismatch in ffc_negative_digit_comp (float slow path)#26
kolemannix merged 1 commit into
kolemannix:mainfrom
redis-performance:fix/negative-digit-comp-vk-upstream

Conversation

@fcostaoliveira

Copy link
Copy Markdown
Contributor

Summary

ffc_negative_digit_comp (the negative-exponent big-integer slow path) builds its rounded-down reference value b with a hardcoded FFC_VALUE_KIND_DOUBLE, then reads 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), which is why it's gone unnoticed. But it's 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") and pass after.
  • Full float exhaustive (test_exhaustive, all 2^32 values) is "all ok" with the fix; a strtof-parity midpoint sweep drops from ~1.07e9 mismatches to 0. make test passes.

Found while using ffc.h downstream (parsing RESP3 doubles in hiredis); reporting upstream since the bug is in the original code.

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 (test_exhaustive, all 2^32 values) is "all ok".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fcostaoliveira added a commit to redis-performance/ffc-agent-workspace that referenced this pull request Jun 9, 2026
)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kolemannix
kolemannix merged commit 38a6c65 into kolemannix:main Jun 15, 2026
6 checks passed
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.

2 participants