You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(posit): fix division bugs and complete regression tests (#534) (#537)
* fix(posit): fix division bugs and complete regression tests (#534)
Two bugs in posit2's division pipeline:
1. Missing setradix() in normalizeDivision's multi-limb path caused
blocksignificand::div() to use wrong radix (bfbits vs divbits),
placing quotient bits 2 positions too high (e.g. 10/2=4).
2. Missing sticky bits in convert() — only nbits+4 fraction bits were
extracted from the blocktriple, silently discarding remaining quotient
bits needed for correct round-to-even decisions (-1 ULP errors).
Also completes the posit2 division regression test suite to match posit1:
- Random tests at levels 2-4 for posit<16,2> through posit<64,4>
- Adversarial worst-case division test functions
- Guards posit1-specific functions in posit_test_suite_randoms.hpp so
both posit and posit1 can share the same test infrastructure.
Verified with both gcc and clang. No regressions in add/sub/mul.
* code hygiene
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
- Cause: The multi-limb path (else branch) of normalizeDivision() did not call tgt.setradix() to set the blocksignificand's radix point. The if constexpr
11
+
fast path used tgt.setbits(raw) which internally calls setradix(), but the else path for wider posits used setblock()/setbit()/bitShift() which do not.
12
+
- Effect: blocksignificand::div() reads lhs.radix() to determine iteration count and quotient bit placement. With the default radixPoint (bfbits=87 instead
13
+
of correct divbits=85 for posit<32,2>), quotient bits were placed 2 positions too high, causing results like 10/2=4.
14
+
- Threshold: Manifested at nbits >= 24 (where divshift >= 64 - fbits, triggering the else branch).
15
+
- Fix: Added tgt.setradix() in the else branch, matching the pattern used in normalizeAddition's else branch at line 1025.
- Cause: The convert() function extracts only nbits + 4 fraction bits from the blocktriple into a blocksignificand for rounding. For DIV, the blocktriple
21
+
has 3*fbits + 4 fraction bits — far more than extracted. The remaining bits were silently discarded without contributing to the sticky bit in convert_().
22
+
- Effect: Round-to-even decisions were wrong in ~0.1-0.3% of cases, producing results exactly 1 ULP below the correct value.
23
+
- Fix: After the extraction loop, check if any blocktriple bits below the extracted range are set via v.any(), and fold them into the lowest bit of the
- Added GenerateWorstCaseDivision, EnumerateToughDivisions, and ToughDivisions2 adversarial test functions (ported from posit1, adapted for posit2's API:
32
+
.get() → to_binary())
33
+
- Added posit<3,2> and posit<3,3> to Level 1 (matching posit1)
34
+
- Level 2: Added VerifyBinaryOperatorThroughRandoms for posit<16,2> and posit<24,2> (1000 randoms)
- Cause: The multi-limb path (else branch) of normalizeDivision() did not call tgt.setradix() to set the blocksignificand's radix point. The if constexpr
11
+
fast path used tgt.setbits(raw) which internally calls setradix(), but the else path for wider posits used setblock()/setbit()/bitShift() which do not.
12
+
- Effect: blocksignificand::div() reads lhs.radix() to determine iteration count and quotient bit placement. With the default radixPoint (bfbits=87 instead
13
+
of correct divbits=85 for posit<32,2>), quotient bits were placed 2 positions too high, causing results like 10/2=4.
14
+
- Threshold: Manifested at nbits >= 24 (where divshift >= 64 - fbits, triggering the else branch).
15
+
- Fix: Added tgt.setradix() in the else branch, matching the pattern used in normalizeAddition's else branch at line 1025.
- Cause: The convert() function extracts only nbits + 4 fraction bits from the blocktriple into a blocksignificand for rounding. For DIV, the blocktriple
21
+
has 3*fbits + 4 fraction bits — far more than extracted. The remaining bits were silently discarded without contributing to the sticky bit in convert_().
22
+
- Effect: Round-to-even decisions were wrong in ~0.1-0.3% of cases, producing results exactly 1 ULP below the correct value.
23
+
- Fix: After the extraction loop, check if any blocktriple bits below the extracted range are set via v.any(), and fold them into the lowest bit of the
- Added GenerateWorstCaseDivision, EnumerateToughDivisions, and ToughDivisions2 adversarial test functions (ported from posit1, adapted for posit2's API:
32
+
.get() → to_binary())
33
+
- Added posit<3,2> and posit<3,3> to Level 1 (matching posit1)
34
+
- Level 2: Added VerifyBinaryOperatorThroughRandoms for posit<16,2> and posit<24,2> (1000 randoms)
0 commit comments