Skip to content

Commit e850f6c

Browse files
authored
math.unsigned: fix div_128() for uint128, add tests (#24869)
1 parent 499283e commit e850f6c

2 files changed

Lines changed: 50 additions & 34 deletions

File tree

‎vlib/math/unsigned/uint128.v‎

Lines changed: 22 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -137,45 +137,33 @@ pub fn mul_128(x Uint128, y Uint128) (Uint128, Uint128) {
137137
}
138138

139139
// div_128 returns u / v
140-
pub fn div_128(hi Uint128, lo Uint128, y_ Uint128) (Uint128, Uint128) {
141-
mut y := y_
142-
if y.is_zero() {
143-
panic('integer divide by zero')
140+
pub fn div_128(a_hi Uint128, a_lo Uint128, b Uint128) (Uint128, Uint128) {
141+
a := uint256_new(a_lo, a_hi)
142+
b_256 := uint256_new(b, uint128_zero)
143+
if a < b_256 {
144+
return uint128_zero, a_lo
144145
}
145-
146-
if y.cmp(hi) <= 0 {
147-
panic('integer overflow')
146+
if b.is_zero() {
147+
panic('Division by zero')
148148
}
149-
150-
s := u32(y.leading_zeros())
151-
y = y.lsh(s)
152-
153-
un32 := hi.lsh(s).or_(lo.rsh(128 - s))
154-
un10 := lo.lsh(s)
155-
mut q1, rhat := un32.quo_rem_64(y.hi)
156-
mut r1 := uint128_from_64(rhat)
157-
tmp := Uint128{r1.lo, un10.hi}
158-
for q1.hi != 0 || q1.mul_64(y.lo).cmp(tmp) > 0 {
159-
q1 = q1.sub_64(1)
160-
r1 = r1.add_64(y.hi)
161-
if r1.hi != 0 {
162-
break
163-
}
149+
if a.hi.is_zero() {
150+
return a.lo.quo_rem(b)
164151
}
152+
shift := u32(b_256.leading_zeros() - a.leading_zeros())
153+
mut af := a
154+
mut bf := b_256.lsh(shift)
155+
mut quotient := uint128_zero
165156

166-
un21 := Uint128{un32.lo, un10.hi}.sub(q1.mul(y))
167-
mut q0, rhat2 := un21.quo_rem_64(y.hi)
168-
mut r0 := uint128_from_64(rhat2)
169-
tmp2 := Uint128{r0.lo, un10.lo}
170-
for q0.hi != 0 || q0.mul_64(y.lo).cmp(tmp2) > 0 {
171-
q0 = q0.sub_64(1)
172-
r0 = r0.add_64(y.hi)
173-
if r0.hi != 0 {
174-
break
175-
}
157+
for _ in 0 .. shift + 1 {
158+
quotient = quotient.lsh(1)
159+
diff_result := bf.add(af.not())
160+
s := u64(i64(diff_result.hi.hi) >> 63)
161+
quotient.lo |= s & 1
162+
mask := uint256_new(uint128_new(s, s), uint128_new(s, s))
163+
af = af.sub(bf.and(mask))
164+
bf = bf.rsh(1)
176165
}
177-
178-
return Uint128{q1.lo, q0.lo}, Uint128{un21.lo, un10.lo}.sub(q0.mul(y)).rsh(s)
166+
return quotient, af.lo
179167
}
180168

181169
// add_64 returns u + v with wraparound semantics

‎vlib/math/unsigned/uint128_test.v‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,31 @@ fn test_quo_rem() {
213213
}
214214
}
215215
}
216+
217+
fn test_div_128() {
218+
for a_lo in 0 .. 4 {
219+
for a_hi in 0 .. 4 {
220+
for b_lo in 0 .. 4 {
221+
for b_hi in 0 .. 4 {
222+
for c_lo in 0 .. 4 {
223+
for c_hi in 0 .. 4 {
224+
a := unsigned.uint128_new(u64(1 << a_lo), u64(1 << a_hi))
225+
b := unsigned.uint128_new(u64(1 << b_lo), u64(1 << b_hi))
226+
c := unsigned.uint128_new(u64(1 << c_lo), u64(1 << c_hi))
227+
if c.cmp(a) <= 0 {
228+
continue
229+
}
230+
aa := unsigned.uint256_new(b, a)
231+
big_a := big.integer_from_string(aa.str())!
232+
big_b := big.integer_from_string(c.str())!
233+
q, r := unsigned.div_128(a, b, c)
234+
big_q, big_r := big_a.div_mod(big_b)
235+
assert q.str() == big_q.str()
236+
assert r.str() == big_r.str()
237+
}
238+
}
239+
}
240+
}
241+
}
242+
}
243+
}

0 commit comments

Comments
 (0)