Skip to content

Preserve NaN sign+payload and tiny-float sign in cbor_encode_half - #412

Merged
PJK merged 3 commits into
masterfrom
fix/nan-half-encoding
Mar 22, 2026
Merged

PJK merged 3 commits into
masterfrom
fix/nan-half-encoding

Conversation

@PJK

@PJK PJK commented Mar 22, 2026

Copy link
Copy Markdown
Owner

Summary

  • cbor_encode_half: all NaN values were encoded as 0x7E00 (positive quiet NaN, zero payload), discarding the sign bit and up to 10 payload bits. Now the top 10 bits of the single-precision mantissa are mapped to the 10-bit half-precision mantissa, preserving both the quiet/signaling distinction and the payload. If the payload fits entirely in the bottom 13 bits (below half-precision resolution), the encoder falls back to a quiet NaN with the sign preserved.
  • cbor_encode_half: very small normal floats with logical_exp < -24 were rounded to +0 regardless of sign. Negative values now correctly round to −0.
  • _cbor_decode_half: NaN values were decoded using the C NAN constant, losing the sign and payload. Now the half-precision NaN bits are used to reconstruct the single-precision float bit pattern directly, enabling true encode/decode round-trip fidelity.

Fixes #215.

Test plan

  • Existing test_half / test_half_special / test_half_infinity still pass
  • New: cbor_encode_half(-1e-25f) → {0xF9, 0x80, 0x00} (negative zero)
  • New: cbor_encode_half(-NAN) → {0xF9, 0xFE, 0x00} (negative quiet NaN)
  • New: NaN with payload in upper mantissa bits (0x7FC02000) → {0xF9, 0x7E, 0x01}, with codec identity verified
  • Full ctest suite: 28/28 pass

🤖 Generated with Claude Code

PJK and others added 3 commits March 22, 2026 21:25
cbor_encode_half previously encoded all NaN values as 0x7E00, losing
the sign bit and up to 10 payload bits. Negative tiny floats (too small
for half precision) were also rounded to +0 instead of -0.

- cbor_encode_half: map the top 10 bits of the single-precision mantissa
  to the half-precision mantissa, preserving both the quiet/signaling bit
  and the payload. Fall back to a quiet NaN only when the payload fits
  entirely in the bottom 13 bits (not representable in half precision).
  Preserve the sign bit in the tiny-float-to-zero rounding path.
- _cbor_decode_half: reconstruct the single-precision float bit pattern
  from the half-precision NaN bits, enabling round-trip fidelity.

Fixes #215

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MIPS legacy uses bit 22 = 0 for quiet NaN (opposite of IEEE 754-2008),
so the C NAN constant has a different bit pattern there. Tests that
asserted cbor_encode_half(NAN) == {0xF9, 0x7E, 0x00} were relying on
the old behavior of normalizing all NaN values to 0x7E00.

Replace specific-byte assertions on NAN, -NAN, and nanf("2") with
structural checks (valid CBOR half marker, exp=0x1F, non-zero mantissa)
plus the existing encode/decode round-trip. Keep a deterministic
specific-byte assertion for an explicit bit pattern (0x7FC02000)
constructed via memcpy, which is independent of the platform's NAN.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@PJK
PJK merged commit bdf710e into master Mar 22, 2026
16 checks passed
@PJK
PJK deleted the fix/nan-half-encoding branch March 22, 2026 21:18
@codecov

codecov Bot commented Mar 22, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.59%. Comparing base (d5222af) to head (184d5db).
⚠️ Report is 13 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #412   +/-   ##
=======================================
  Coverage   99.59%   99.59%           
=======================================
  Files          20       20           
  Lines        1737     1744    +7     
=======================================
+ Hits         1730     1737    +7     
  Misses          7        7           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

PJK added a commit that referenced this pull request Sep 16, 2026
* Preserve NaN sign and payload in single and double encoders

cbor_encode_single and cbor_encode_double replaced every NaN with the
canonical quiet NaN, discarding the sign and payload, while
cbor_encode_half has preserved them since #412. Copy the bit pattern
verbatim at all widths so that decode/encode round trips of NaNs are
lossless. The comment claiming there is no standard way to extract the
bits was contradicted by the union bit-cast on the next line.

The canonicalization dated from #329, where exact-byte tests on the C
NAN constant failed on MIPS legacy-NaN hosts. Follow #412 instead: test
NAN and friends structurally (sign, exponent, non-zero mantissa, round
trip) and test exact bytes only with memcpy-built bit patterns.

Reconcile the test vector list: the four quiet NaNs with payloads now
pass outright; the eight signaling NaNs join the half-float ones as
platform-dependent since x87 quiets them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Link PR number in changelog

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Simplify NaN round-trip helpers

Take the expected sign bit as a parameter and let the decoder plus isnan
establish that the buffer holds a NaN, instead of branching on the sign
and checking exponent and mantissa bits by hand.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <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.

Make cbor_encode_half respect NaN data bits

1 participant

⌂ 换网址