Skip to content

Commit 13de5e7

Browse files
bmkesslergriesemer
authored andcommitted
math/bits: add extended precision Add, Sub, Mul, Div
Port math/big pure go versions of add-with-carry, subtract-with-borrow, full-width multiply, and full-width divide. Updates #24813 Change-Id: Ifae5d2f6ee4237137c9dcba931f69c91b80a4b1c Reviewed-on: https://go-review.googlesource.com/123157 Reviewed-by: Robert Griesemer <[email protected]> Run-TryBot: Robert Griesemer <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent ef7212e commit 13de5e7

File tree

2 files changed

+460
-0
lines changed

2 files changed

+460
-0
lines changed

src/math/bits/bits.go

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,3 +328,197 @@ func Len64(x uint64) (n int) {
328328
}
329329
return n + int(len8tab[x])
330330
}
331+
332+
// --- Add with carry ---
333+
334+
// Add returns the sum with carry of x, y and carry: sum = x + y + carry.
335+
// The carry input must be 0 or 1; otherwise the behavior is undefined.
336+
// The carryOut output is guaranteed to be 0 or 1.
337+
func Add(x, y, carry uint) (sum, carryOut uint) {
338+
yc := y + carry
339+
sum = x + yc
340+
if sum < x || yc < y {
341+
carryOut = 1
342+
}
343+
return
344+
}
345+
346+
// Add32 returns the sum with carry of x, y and carry: sum = x + y + carry.
347+
// The carry input must be 0 or 1; otherwise the behavior is undefined.
348+
// The carryOut output is guaranteed to be 0 or 1.
349+
func Add32(x, y, carry uint32) (sum, carryOut uint32) {
350+
yc := y + carry
351+
sum = x + yc
352+
if sum < x || yc < y {
353+
carryOut = 1
354+
}
355+
return
356+
}
357+
358+
// Add64 returns the sum with carry of x, y and carry: sum = x + y + carry.
359+
// The carry input must be 0 or 1; otherwise the behavior is undefined.
360+
// The carryOut output is guaranteed to be 0 or 1.
361+
func Add64(x, y, carry uint64) (sum, carryOut uint64) {
362+
yc := y + carry
363+
sum = x + yc
364+
if sum < x || yc < y {
365+
carryOut = 1
366+
}
367+
return
368+
}
369+
370+
// --- Subtract with borrow ---
371+
372+
// Sub returns the difference of x, y and borrow: diff = x - y - borrow.
373+
// The borrow input must be 0 or 1; otherwise the behavior is undefined.
374+
// The borrowOut output is guaranteed to be 0 or 1.
375+
func Sub(x, y, borrow uint) (diff, borrowOut uint) {
376+
yb := y + borrow
377+
diff = x - yb
378+
if diff > x || yb < y {
379+
borrowOut = 1
380+
}
381+
return
382+
}
383+
384+
// Sub32 returns the difference of x, y and borrow, diff = x - y - borrow.
385+
// The borrow input must be 0 or 1; otherwise the behavior is undefined.
386+
// The borrowOut output is guaranteed to be 0 or 1.
387+
func Sub32(x, y, borrow uint32) (diff, borrowOut uint32) {
388+
yb := y + borrow
389+
diff = x - yb
390+
if diff > x || yb < y {
391+
borrowOut = 1
392+
}
393+
return
394+
}
395+
396+
// Sub64 returns the difference of x, y and borrow: diff = x - y - borrow.
397+
// The borrow input must be 0 or 1; otherwise the behavior is undefined.
398+
// The borrowOut output is guaranteed to be 0 or 1.
399+
func Sub64(x, y, borrow uint64) (diff, borrowOut uint64) {
400+
yb := y + borrow
401+
diff = x - yb
402+
if diff > x || yb < y {
403+
borrowOut = 1
404+
}
405+
return
406+
}
407+
408+
// --- Full-width multiply ---
409+
410+
// Mul returns the full-width product of x and y: (hi, lo) = x * y
411+
// with the product bits' upper half returned in hi and the lower
412+
// half returned in lo.
413+
func Mul(x, y uint) (hi, lo uint) {
414+
if UintSize == 32 {
415+
h, l := Mul32(uint32(x), uint32(y))
416+
return uint(h), uint(l)
417+
}
418+
h, l := Mul64(uint64(x), uint64(y))
419+
return uint(h), uint(l)
420+
}
421+
422+
// Mul32 returns the 64-bit product of x and y: (hi, lo) = x * y
423+
// with the product bits' upper half returned in hi and the lower
424+
// half returned in lo.
425+
func Mul32(x, y uint32) (hi, lo uint32) {
426+
tmp := uint64(x) * uint64(y)
427+
hi, lo = uint32(tmp>>32), uint32(tmp)
428+
return
429+
}
430+
431+
// Mul64 returns the 128-bit product of x and y: (hi, lo) = x * y
432+
// with the product bits' upper half returned in hi and the lower
433+
// half returned in lo.
434+
func Mul64(x, y uint64) (hi, lo uint64) {
435+
const mask32 = 1<<32 - 1
436+
x0 := x & mask32
437+
x1 := x >> 32
438+
y0 := y & mask32
439+
y1 := y >> 32
440+
w0 := x0 * y0
441+
t := x1*y0 + w0>>32
442+
w1 := t & mask32
443+
w2 := t >> 32
444+
w1 += x0 * y1
445+
hi = x1*y1 + w2 + w1>>32
446+
lo = x * y
447+
return
448+
}
449+
450+
// --- Full-width divide ---
451+
452+
// Div returns the quotient and remainder of (hi, lo) divided by y:
453+
// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper
454+
// half in parameter hi and the lower half in parameter lo.
455+
// hi must be < y otherwise the behavior is undefined (the quotient
456+
// won't fit into quo).
457+
func Div(hi, lo, y uint) (quo, rem uint) {
458+
if UintSize == 32 {
459+
q, r := Div32(uint32(hi), uint32(lo), uint32(y))
460+
return uint(q), uint(r)
461+
}
462+
q, r := Div64(uint64(hi), uint64(lo), uint64(y))
463+
return uint(q), uint(r)
464+
}
465+
466+
// Div32 returns the quotient and remainder of (hi, lo) divided by y:
467+
// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper
468+
// half in parameter hi and the lower half in parameter lo.
469+
// hi must be < y otherwise the behavior is undefined (the quotient
470+
// won't fit into quo).
471+
func Div32(hi, lo, y uint32) (quo, rem uint32) {
472+
z := uint64(hi)<<32 | uint64(lo)
473+
quo, rem = uint32(z/uint64(y)), uint32(z%uint64(y))
474+
return
475+
}
476+
477+
// Div64 returns the quotient and remainder of (hi, lo) divided by y:
478+
// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper
479+
// half in parameter hi and the lower half in parameter lo.
480+
// hi must be < y otherwise the behavior is undefined (the quotient
481+
// won't fit into quo).
482+
func Div64(hi, lo, y uint64) (quo, rem uint64) {
483+
const (
484+
two32 = 1 << 32
485+
mask32 = two32 - 1
486+
)
487+
if hi >= y {
488+
return 1<<64 - 1, 1<<64 - 1
489+
}
490+
491+
s := uint(LeadingZeros64(y))
492+
y <<= s
493+
494+
yn1 := y >> 32
495+
yn0 := y & mask32
496+
un32 := hi<<s | lo>>(64-s)
497+
un10 := lo << s
498+
un1 := un10 >> 32
499+
un0 := un10 & mask32
500+
q1 := un32 / yn1
501+
rhat := un32 - q1*yn1
502+
503+
for q1 >= two32 || q1*yn0 > two32*rhat+un1 {
504+
q1--
505+
rhat += yn1
506+
if rhat >= two32 {
507+
break
508+
}
509+
}
510+
511+
un21 := un32*two32 + un1 - q1*y
512+
q0 := un21 / yn1
513+
rhat = un21 - q0*yn1
514+
515+
for q0 >= two32 || q0*yn0 > two32*rhat+un0 {
516+
q0--
517+
rhat += yn1
518+
if rhat >= two32 {
519+
break
520+
}
521+
}
522+
523+
return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s
524+
}

0 commit comments

Comments
 (0)