Skip to content

Commit b39d5d5

Browse files
handle multi-word division edge case
Signed-off-by: Andrew Whitehead <cywolf@gmail.com>
1 parent 46f2498 commit b39d5d5

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

src/uint/div_limb.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,12 @@ pub(crate) const fn div3by2(
151151
v: Word,
152152
) -> (Word, WideWord) {
153153
let d = word::join(d0, d1);
154+
let u_hi = word::join(u1, u2);
154155

155156
debug_assert!(d >= (1 << (WideWord::BITS - 1)), "divisor top bit unset");
156-
debug_assert!(word::join(u1, u2) < d, "dividend >= divisor");
157+
debug_assert!(u_hi <= d, "dividend > divisor");
157158

158-
let q = (v as WideWord * u2 as WideWord) + word::join(u1, u2);
159+
let q = (v as WideWord * u2 as WideWord) + u_hi;
159160
let q1w = q >> Word::BITS;
160161
let r1 = u1.wrapping_sub((q1w as Word).wrapping_mul(d1));
161162
let t = d0 as WideWord * q1w;
@@ -170,6 +171,13 @@ pub(crate) const fn div3by2(
170171
let q1 = word::select(q1, q1.wrapping_add(1), r_ge_d);
171172
let r = word::select_wide(r, r.wrapping_sub(d), r_ge_d);
172173

174+
// When the leading dividend word equals the leading divisor word, cap the quotient
175+
// at WideWord::MAX and update the remainder. This differs from the original algorithm
176+
// but is required for multi-word division.
177+
let maxed = word::choice_from_wide_eq(u_hi, d);
178+
let q1 = word::select(q1, Word::MAX, maxed);
179+
let r = word::select_wide(r, d.saturating_add(u0 as WideWord), maxed);
180+
173181
(q1, r)
174182
}
175183

src/word.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ pub(crate) const fn choice_from_eq(x: Word, y: Word) -> Choice {
6464
choice_from_nz(x ^ y).not()
6565
}
6666

67+
/// Returns the truthy value if `x == y`, and the falsy value otherwise.
68+
#[inline]
69+
pub(crate) const fn choice_from_wide_eq(x: WideWord, y: WideWord) -> Choice {
70+
choice_from_wide_nz(x ^ y).not()
71+
}
72+
6773
/// Returns the truthy value if `x > y`, and the falsy value otherwise.
6874
#[inline]
6975
pub(crate) const fn choice_from_gt(x: Word, y: Word) -> Choice {
@@ -95,6 +101,12 @@ pub(crate) const fn choice_from_nz(value: Word) -> Choice {
95101
choice_from_lsb((value | value.wrapping_neg()) >> (Word::BITS - 1))
96102
}
97103

104+
/// Returns the truthy value if `value != 0`, and the falsy value otherwise.
105+
#[inline]
106+
pub(crate) const fn choice_from_wide_nz(value: WideWord) -> Choice {
107+
choice_from_lsb(((value | value.wrapping_neg()) >> (WideWord::BITS - 1)) as Word)
108+
}
109+
98110
/// Return `b` if `self` is truthy, otherwise return `a`.
99111
#[inline]
100112
pub(crate) const fn select(a: Word, b: Word, choice: Choice) -> Word {

0 commit comments

Comments
 (0)