Skip to content

Commit 0265889

Browse files
Adding div64
1 parent e642226 commit 0265889

1 file changed

Lines changed: 146 additions & 2 deletions

File tree

  • compiler/natives/src/math/bits

compiler/natives/src/math/bits/bits.go

Lines changed: 146 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,153 @@ func Div32(hi, lo, y uint32) (quo, rem uint32) {
320320
return q1*two16 + q0, (un21*two16 + un0 - q0*y) >> s
321321
}
322322

323-
//gopherjs:remove
323+
//gopherjs:replace
324324
func Div64(hi, lo, y uint64) (quo, rem uint64) {
325-
// TODO: Agent insert code here
325+
// Reference: "The Art of Computer Programming" (TAoCP) Vol. 2 by Knuth
326+
// (a copy can be found at https://github.com/Code42Cate/The-Art-of-Computer-Programming/blob/master/Volume2.pdf)
327+
// describes Algorithm D (Division of nonnegative integers) in 4.3.1 starting on page 257.
328+
//
329+
// This code is similar to the original math/bits.Div64 with all arithmetic
330+
// operating on 32-bit halves to avoid using uint64 operations that we have
331+
// to emulate in JS.
332+
yHi := js.Uint64High(y)
333+
yLo := js.Uint64Low(y)
334+
if yHi == 0 && yLo == 0 {
335+
panic(divideError)
336+
}
337+
hiHi := js.Uint64High(hi)
338+
hiLo := js.Uint64Low(hi)
339+
if yHi < hiHi || (yHi == hiHi && yLo <= hiLo) {
340+
panic(overflowError)
341+
}
342+
loHi := js.Uint64High(lo)
343+
loLo := js.Uint64Low(lo)
344+
345+
// Fast path: divisor fits in 32 bits. The y > hi precondition forces
346+
// hiHi == 0 and hiLo < yLo, so neither Div32 below can overflow.
347+
if yHi == 0 {
348+
q1, r1 := Div32(hiLo, loHi, yLo)
349+
q0, r0 := Div32(r1, loLo, yLo)
350+
return js.MakeUint64(float64(q1), float64(q0)),
351+
js.MakeUint64(0, float64(r0))
352+
}
353+
354+
// General case: yHi != 0 (full 64-bit divisor).
355+
// Normalize so the divisor's top bit is set; s is in [0, 31].
356+
s := uint(LeadingZeros32(yHi))
357+
rs := 32 - s
358+
359+
// Shifted divisor Y = y << s = (Yn1:Yn0).
360+
// Shifted dividend U = (hi:lo) << s = (un32Hi:un32Lo:un1:un0).
361+
// Because hi < y (precondition), hi << s still fits in 64 bits.
362+
var Yn1, Yn0, un32Hi, un32Lo, un1, un0 uint32
363+
if s == 0 {
364+
Yn1, Yn0 = yHi, yLo
365+
un32Hi, un32Lo = hiHi, hiLo
366+
un1, un0 = loHi, loLo
367+
} else {
368+
Yn1 = yHi<<s | yLo>>rs
369+
Yn0 = yLo << s
370+
un32Hi = hiHi<<s | hiLo>>rs
371+
un32Lo = hiLo<<s | loHi>>rs
372+
un1 = loHi<<s | loLo>>rs
373+
un0 = loLo << s
374+
}
375+
376+
// --- First quotient digit q1 ≈ (un32Hi:un32Lo) / Yn1 ---
377+
// Precondition un32 < Y gives un32Hi <= Yn1. When un32Hi == Yn1 the
378+
// true digit is 2^32 or 2^32+1; Knuth's loop implicitly decrements it
379+
// to 2^32-1 with rhat = un32Lo + Yn1. If that sum overflows uint32 the
380+
// loop's "rhat >= two32" early-exit fires and no further adjustment is
381+
// possible from 32-bit rhat, so we mark skipAdj.
382+
var q1, rhat uint32
383+
var skipAdj bool
384+
if un32Hi >= Yn1 {
385+
q1 = 0xFFFFFFFF
386+
sum, carry := Add32(un32Lo, Yn1, 0)
387+
if carry != 0 {
388+
skipAdj = true
389+
} else {
390+
rhat = sum
391+
}
392+
} else {
393+
q1, rhat = Div32(un32Hi, un32Lo, Yn1)
394+
}
395+
396+
// Track q1 * Yn0 incrementally across the correction loop so we avoid
397+
// re-multiplying every iteration and can reuse the final value for un21.
398+
qynHi, qynLo := Mul32(q1, Yn0)
399+
if !skipAdj {
400+
for qynHi > rhat || (qynHi == rhat && qynLo > un1) {
401+
q1--
402+
if qynLo < Yn0 {
403+
qynHi--
404+
}
405+
qynLo -= Yn0
406+
sum, carry := Add32(rhat, Yn1, 0)
407+
if carry != 0 {
408+
break
409+
}
410+
rhat = sum
411+
}
412+
}
413+
414+
// un21 = (un32:un1) - q1*y, mod 2^64.
415+
// (un32 << 32) mod 2^64 = (un32Lo : 0), so the top half of un21 is
416+
// computed from un32Lo (not un32Hi). q1*y mod 2^64 has high 32 bits
417+
// q1*Yn1 + carry(q1*Yn0); both intentionally wrap modulo 2^32.
418+
qyHi := q1*Yn1 + qynHi
419+
un21Lo := un1 - qynLo
420+
un21Hi := un32Lo - qyHi
421+
if un1 < qynLo {
422+
un21Hi--
423+
}
424+
425+
// --- Second quotient digit q0 ≈ (un21Hi:un21Lo) / Yn1 ---
426+
var q0 uint32
427+
skipAdj = false
428+
if un21Hi >= Yn1 {
429+
q0 = 0xFFFFFFFF
430+
sum, carry := Add32(un21Lo, Yn1, 0)
431+
if carry != 0 || un21Hi > Yn1 {
432+
skipAdj = true
433+
} else {
434+
rhat = sum
435+
}
436+
} else {
437+
q0, rhat = Div32(un21Hi, un21Lo, Yn1)
438+
}
439+
440+
qynHi, qynLo = Mul32(q0, Yn0)
441+
if !skipAdj {
442+
for qynHi > rhat || (qynHi == rhat && qynLo > un0) {
443+
q0--
444+
if qynLo < Yn0 {
445+
qynHi--
446+
}
447+
qynLo -= Yn0
448+
sum, carry := Add32(rhat, Yn1, 0)
449+
if carry != 0 {
450+
break
451+
}
452+
rhat = sum
453+
}
454+
}
455+
456+
// Remainder = (un21:un0) - q0*y, mod 2^64, then >> s to denormalize.
457+
qyHi2 := q0*Yn1 + qynHi
458+
remLo := un0 - qynLo
459+
remHi := un21Lo - qyHi2
460+
if un0 < qynLo {
461+
remHi--
462+
}
463+
if s != 0 {
464+
remLo = remLo>>s | remHi<<rs
465+
remHi = remHi >> s
466+
}
467+
468+
return js.MakeUint64(float64(q1), float64(q0)),
469+
js.MakeUint64(float64(remHi), float64(remLo))
326470
}
327471

328472
//gopherjs:replace

0 commit comments

Comments
 (0)