Skip to content

fix(cfloat): native trunc/floor/ceil/round (correct for >53-bit significands)#1028

Merged
Ravenwater merged 1 commit into
mainfrom
fix/issue-1026-cfloat-floor-wide
May 28, 2026
Merged

fix(cfloat): native trunc/floor/ceil/round (correct for >53-bit significands)#1028
Ravenwater merged 1 commit into
mainfrom
fix/issue-1026-cfloat-floor-wide

Conversation

@Ravenwater
Copy link
Copy Markdown
Contributor

@Ravenwater Ravenwater commented May 28, 2026

Summary

trunc/round/floor/ceil for cfloat<> computed std::X(double(x)). That is wrong for cfloats with more than 53 significand bits: any integer above 2^53 loses its low bits in the double conversion, so e.g. floor(2^60 + 8) did not return 2^60 + 8 (#1026). Reimplemented directly on the cfloat encoding.

The fix (truncate.hpp)

  • trunc — clear the fraction bits below 2^0 per the unbiased scale (scale >= fbits → integral; subnormal or scale < 0 → signed zero). Pure bit operation.
  • floor / ceiltrunc, then -/+ 1 for the negative/positive non-integral case.
  • round — ties away from zero, decided from the 2^-1 fraction bit for |x| >= 1 and from 2|x| >= 1 for |x| < 1. Deliberately never constructs 0.5, which some low-range cfloats (e.g. cfloat<8,2>) cannot represent -- an early version did and regressed those.

All derived ops use cfloat's own (correctly rounded) arithmetic/comparisons; no double round-trip.

Test (static/float/cfloat/math/truncate.cpp)

  • Added exhaustive trunc and round coverage (was floor/ceil only).
  • Added a wide-precision verifier on cfloat<128,15>: 2^60+8 ± exact fractions, both signs, checked against cfloat-constructed references (no double).
  • Fixed the result accumulation (=+=, a latent bug that hid all but the last sub-test).

Test Results

Target gcc clang
cfloat_truncate (exhaustive narrow + wide cfloat<128,15>) PASS PASS
cfloat_fractional (fmod, regression check) PASS PASS

Exhaustive cfloat<8,2>/half/bfloat_t match the representable std reference; wide cfloat<128,15> exact.

Notes

Test plan

  • Fast CI passes
  • Promote to ready when satisfied

Resolves #1026

Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Updated truncation and rounding functions with improved edge case handling and special value support (NaN, Inf, zeros).
  • Tests

    • Expanded test coverage for mathematical truncation and rounding functions across additional numeric types.

Review Change Stack

…ficands)

trunc/round/floor/ceil computed `std::X(double(x))`, which is wrong for cfloats
with more than 53 significand bits: any integer above 2^53 loses its low bits in
the double conversion, so e.g. floor(2^60+8) did not return 2^60+8 (#1026).

Now implemented directly on the cfloat encoding:
- trunc: clear the fraction bits below 2^0 per the unbiased scale (scale>=fbits ->
  integral; subnormal or scale<0 -> signed zero). Pure bit op, no arithmetic.
- floor/ceil: trunc, then -/+ 1 for the negative/positive non-integral case.
- round: ties away from zero, decided from the 2^-1 fraction bit for |x|>=1 and
  from 2|x|>=1 for |x|<1 -- never constructing 0.5, which some low-range cfloats
  (e.g. cfloat<8,2>) cannot represent.

Test (static/float/cfloat/math/truncate.cpp): added exhaustive trunc/round
coverage (was floor/ceil only), a wide-precision verifier on cfloat<128,15>
(2^60+8 +/- exact fractions, both signs, vs cfloat-constructed references -- no
double), and fixed the result accumulation (`=` -> `+=`).

Verified gcc + clang: exhaustive cfloat<8,2>/half/bfloat_t match the
representable std reference; wide cfloat<128,15> exact; cfloat_fractional (fmod)
still passes.

Resolves #1026

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

This PR fixes truncation functions (trunc, floor, ceil, round) in cfloat to operate natively on bit encoding instead of routing through double, solving precision loss for wide-significand values exceeding 53-bit representation. The header implements new bit-level logic; the test suite validates correctness and adds wide-precision regression coverage.

Changes

cfloat Truncation Fix

Layer / File(s) Summary
Direct cfloat truncation operations
include/sw/universal/number/cfloat/math/functions/truncate.hpp
New trunc clears fractional bits by unbiased scale with NaN/Inf/zero passthrough and signed-zero handling. floor and ceil derive from trunc with sign-aware ±1 adjustment for non-integral values. round applies nearest-ties-away-from-zero using fraction-bit testing for normal scales and exact 2*|x| >= 1 comparison for small magnitudes. All previous double-routing logic removed.
Test verification and regression suite
static/float/cfloat/math/truncate.cpp
VerifyFloor and VerifyCeil updated to inline reference via std::floor/ceil(float(a)) and simplified mismatch reporting. New VerifyTrunc and VerifyRound functions with matching patterns. Added VerifyWideTruncation to validate all four functions on wide-significand integers (cfloat<128,15> for issue #1026). Test runner now executes floor/ceil/trunc/round for cfloat<8,2>, half, and bfloat_t in manual and regression modes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

bug

Poem

🐰 A rabbit hops through bits with care,
No double-loss in cfloat's lair—
Wide significands now stay true,
Floor, ceil, and round see it through!
Precision wide, from 2^0 to far,
No rounding trips to near and far. 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(cfloat): native trunc/floor/ceil/round (correct for >53-bit significands)' accurately summarizes the main change: replacing double-based implementations with native bit operations to handle large cfloat significands correctly.
Linked Issues check ✅ Passed The PR fully implements native trunc/floor/ceil/round directly on cfloat encoding (#1026), adds exhaustive test coverage with wide-precision verification via cfloat<128,15> (#1026), and correctly validates against cfloat-constructed references without double round-trip.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing native truncation/rounding for cfloat and corresponding test updates; no unrelated modifications detected beyond the stated PR objectives.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-1026-cfloat-floor-wide

Comment @coderabbitai help to get the list of available commands and usage tips.

@Ravenwater Ravenwater self-assigned this May 28, 2026
@Ravenwater Ravenwater added this to the V4 milestone May 28, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
static/float/cfloat/math/truncate.cpp (2)

161-167: 💤 Low value

Manual testing section doesn't test all four functions.

The MANUAL_TESTING block only tests floor and ceil, while the regression block tests all four functions (floor, ceil, trunc, round). For consistency, consider adding VerifyTrunc and VerifyRound calls here as well.

📝 Suggested addition
 	nrOfFailedTestCases += ReportTestResult(VerifyFloor< cfloat<8, 2, uint8_t> >(reportTestCases), "floor", "cfloat<8,2>");
 	nrOfFailedTestCases += ReportTestResult(VerifyCeil < cfloat<8, 2, uint8_t> >(reportTestCases), "ceil ", "cfloat<8,2>");
+	nrOfFailedTestCases += ReportTestResult(VerifyTrunc< cfloat<8, 2, uint8_t> >(reportTestCases), "trunc", "cfloat<8,2>");
+	nrOfFailedTestCases += ReportTestResult(VerifyRound< cfloat<8, 2, uint8_t> >(reportTestCases), "round", "cfloat<8,2>");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@static/float/cfloat/math/truncate.cpp` around lines 161 - 167, The
MANUAL_TESTING block only calls VerifyFloor and VerifyCeil; add calls to
VerifyTrunc< cfloat<8, 2, uint8_t> > and VerifyRound< cfloat<8, 2, uint8_t> >
and accumulate their results into nrOfFailedTestCases using ReportTestResult,
mirroring the existing ReportTestResult pattern for "trunc" and "round" labels;
keep the ReportTestSuiteResults(test_suite, nrOfFailedTestCases) and return
EXIT_SUCCESS unchanged.

12-16: 💤 Low value

Comment mentions 53-bit significands but code uses float (24-bit precision).

The comment states these verifiers are valid for types with "≤ 53 significand bits," but the reference computation uses float(a) which only has ~24 bits of mantissa precision. This is correct for the current test types (all have < 24 significand bits), but the comment is misleading if someone adds a test type with 25-53 bits.

Consider either updating the comment to reflect the actual float limitation, or using double for the reference to match the comment's claim.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@static/float/cfloat/math/truncate.cpp` around lines 12 - 16, The comment
claims the verifier uses an exact reference for types with "≤ 53 significand
bits" but the code uses float(a) (≈24-bit mantissa); either correct the comment
or use double for the reference. Fix by replacing float(a) with double(a) in the
small-cfloat exhaustive verifier where native trunc/floor/ceil/round are
compared, and update the nearby comment to state that double is used and is
exact for ≤53-bit significands (leave VerifyWideTruncation as-is for wider
cases). Ensure you modify the same verifier function/block that currently calls
float(a) and adjust the comment text to reflect the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@static/float/cfloat/math/truncate.cpp`:
- Around line 161-167: The MANUAL_TESTING block only calls VerifyFloor and
VerifyCeil; add calls to VerifyTrunc< cfloat<8, 2, uint8_t> > and VerifyRound<
cfloat<8, 2, uint8_t> > and accumulate their results into nrOfFailedTestCases
using ReportTestResult, mirroring the existing ReportTestResult pattern for
"trunc" and "round" labels; keep the ReportTestSuiteResults(test_suite,
nrOfFailedTestCases) and return EXIT_SUCCESS unchanged.
- Around line 12-16: The comment claims the verifier uses an exact reference for
types with "≤ 53 significand bits" but the code uses float(a) (≈24-bit
mantissa); either correct the comment or use double for the reference. Fix by
replacing float(a) with double(a) in the small-cfloat exhaustive verifier where
native trunc/floor/ceil/round are compared, and update the nearby comment to
state that double is used and is exact for ≤53-bit significands (leave
VerifyWideTruncation as-is for wider cases). Ensure you modify the same verifier
function/block that currently calls float(a) and adjust the comment text to
reflect the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 009d4528-26d7-4af6-849b-5af460868471

📥 Commits

Reviewing files that changed from the base of the PR and between 3c8802f and 5cdb8e0.

📒 Files selected for processing (2)
  • include/sw/universal/number/cfloat/math/functions/truncate.hpp
  • static/float/cfloat/math/truncate.cpp

@Ravenwater Ravenwater marked this pull request as ready for review May 28, 2026 15:52
@Ravenwater Ravenwater merged commit c04efce into main May 28, 2026
34 checks passed
@Ravenwater Ravenwater deleted the fix/issue-1026-cfloat-floor-wide branch May 28, 2026 16:17
@coveralls
Copy link
Copy Markdown

Coverage Report for CI Build 26585831192

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage increased (+0.008%) to 84.007%

Details

  • Coverage increased (+0.008%) from the base build.
  • Patch coverage: 33 of 33 lines across 1 file are fully covered (100%).
  • 10 coverage regressions across 2 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

10 previously-covered lines in 2 files lost coverage.

File Lines Losing Coverage Coverage
include/sw/universal/number/cfloat/cfloat_impl.hpp 8 93.54%
include/sw/universal/number/posito/posito_impl.hpp 2 89.78%

Coverage Stats

Coverage Status
Relevant Lines: 55857
Covered Lines: 46924
Line Coverage: 84.01%
Coverage Strength: 5780085.0 hits per line

💛 - Coveralls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

cfloat: floor/trunc/round incorrect for large integers in wide cfloat (>53-bit significand)

2 participants