Skip to content

Commit 1dc4596

Browse files
committed
math/big: add floor and ceiling division to Int
Adds methods Floor/FloorMod for floor division and Ceil/CeilMod for ceiling division. Fixes #76821
1 parent 1b291b7 commit 1dc4596

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed

api/next/76821.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pkg math/big, method (*Int) Floor(*Int, *Int) *Int #76821
2+
pkg math/big, method (*Int) FloorMod(*Int, *Int, *Int) (*Int, *Int) #76821
3+
pkg math/big, method (*Int) Ceil(*Int, *Int) *Int #76821
4+
pkg math/big, method (*Int) CeilMod(*Int, *Int, *Int) (*Int, *Int) #76821
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<!-- go.dev/issue/76821 -->
2+
[Int] now has [Int.Floor], [Int.FloorMod], [Int.Ceil] and [Int.CeilMod] methods.

src/math/big/int.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,3 +1308,86 @@ func (z *Int) Sqrt(x *Int) *Int {
13081308
z.abs = z.abs.sqrt(nil, x.abs)
13091309
return z
13101310
}
1311+
1312+
// Floor sets z to ⌊x/y⌋ for y != 0 and returns z.
1313+
// If y == 0, a division-by-zero run-time panic occurs.
1314+
// Floor implements floor division (unlike Go); see [Int.FloorMod] for more details.
1315+
func (z *Int) Floor(x, y *Int) *Int {
1316+
y0 := y // save y
1317+
if z == y || alias(z.abs, y.abs) {
1318+
y0 = new(Int).Set(y)
1319+
}
1320+
var m Int
1321+
z.DivMod(x, y, &m)
1322+
if len(m.abs) == 0 || !y0.neg {
1323+
return z
1324+
}
1325+
z.Sub(z, intOne)
1326+
return z
1327+
1328+
}
1329+
1330+
// FloorMod sets z to ⌊x/y⌋ and m to x mod y
1331+
// for y != 0 and returns the pair (z, m).
1332+
// If y == 0, a division-by-zero run-time panic occurs.
1333+
//
1334+
// FloorMod implements floor division and modulus (unlike Go):
1335+
//
1336+
// q = ⌊x/y⌋ and
1337+
// m = x - y*q where 0 <= |m| < |y|, sgn(m) = sgn(y)
1338+
//
1339+
// See [Int.QuoRem] for T-division and modulus (like Go).
1340+
func (z *Int) FloorMod(x, y, m *Int) (*Int, *Int) {
1341+
y0 := y // save y
1342+
if z == y || alias(z.abs, y.abs) {
1343+
y0 = new(Int).Set(y)
1344+
}
1345+
z.DivMod(x, y, m)
1346+
if len(m.abs) == 0 || !y0.neg {
1347+
return z, m
1348+
}
1349+
z.Sub(z, intOne)
1350+
m.Add(m, y0)
1351+
return z, m
1352+
}
1353+
1354+
// Ceil sets z to ⌈x/y⌉ for y != 0 and returns z.
1355+
// If y == 0, a division-by-zero run-time panic occurs.
1356+
// Ceil implements ceiling division (unlike Go); see [Int.CeilMod] for more details.
1357+
func (z *Int) Ceil(x, y *Int) *Int {
1358+
y0 := y // save y
1359+
if z == y || alias(z.abs, y.abs) {
1360+
y0 = new(Int).Set(y)
1361+
}
1362+
var m Int
1363+
z.DivMod(x, y, &m)
1364+
if len(m.abs) == 0 || y0.neg {
1365+
return z
1366+
}
1367+
z.Add(z, intOne)
1368+
return z
1369+
}
1370+
1371+
// CeilMod sets z to ⌈x/y⌉ and m to x mod y
1372+
// for y != 0 and returns the pair (z, m).
1373+
// If y == 0, a division-by-zero run-time panic occurs.
1374+
//
1375+
// CeilMod implements ceiling division and modulus (unlike Go):
1376+
//
1377+
// q = ⌈x/y⌉ and
1378+
// m = x - y*q where 0 <= |m| < |y|, sgn(m) = -sgn(y)
1379+
//
1380+
// See [Int.QuoRem] for T-division and modulus (like Go).
1381+
func (z *Int) CeilMod(x, y, m *Int) (*Int, *Int) {
1382+
y0 := y // save y
1383+
if z == y || alias(z.abs, y.abs) {
1384+
y0 = new(Int).Set(y)
1385+
}
1386+
z.DivMod(x, y, m)
1387+
if len(m.abs) == 0 || y0.neg {
1388+
return z, m
1389+
}
1390+
z.Add(z, intOne)
1391+
m.Sub(m, y0)
1392+
return z, m
1393+
}

src/math/big/int_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,3 +2010,75 @@ func TestFloat64(t *testing.T) {
20102010
}
20112011
}
20122012
}
2013+
2014+
func TestIntFloorMod(t *testing.T) {
2015+
for i, test := range []struct {
2016+
x, y, q, m int64
2017+
}{
2018+
{0, 1, 0, 0},
2019+
{0, -1, 0, 0},
2020+
{1, 1, 1, 0},
2021+
{1, -1, -1, 0},
2022+
{-1, 1, -1, 0},
2023+
{-1, -1, 1, 0},
2024+
{1, 3, 0, 1},
2025+
{-1, 3, -1, 2},
2026+
{1, -3, -1, -2},
2027+
{-1, -3, 0, -1},
2028+
// examples from spec#Arithmetic_operators
2029+
{5, 3, 1, 2},
2030+
{-5, 3, -2, 1},
2031+
{5, -3, -2, -1},
2032+
{-5, -3, 1, -2},
2033+
{1, 2, 0, 1},
2034+
{8, 4, 2, 0},
2035+
} {
2036+
if test.q*test.y+test.m != test.x {
2037+
t.Errorf("#%v invalid test, q*y + m != x", i)
2038+
}
2039+
x := NewInt(test.x)
2040+
y := NewInt(test.y)
2041+
m := new(Int)
2042+
q, _ := x.FloorMod(x, y, m)
2043+
if q.Cmp(NewInt(test.q)) != 0 || m.Cmp(NewInt(test.m)) != 0 {
2044+
t.Errorf("#%v got q=%v m=%v, want q=%v m=%v", i, q, m, test.q, test.m)
2045+
}
2046+
}
2047+
2048+
}
2049+
2050+
func TestIntCeilMod(t *testing.T) {
2051+
for i, test := range []struct {
2052+
x, y, q, m int64
2053+
}{
2054+
{0, 1, 0, 0},
2055+
{0, -1, 0, 0},
2056+
{1, 1, 1, 0},
2057+
{1, -1, -1, 0},
2058+
{-1, 1, -1, 0},
2059+
{-1, -1, 1, 0},
2060+
{1, 3, 1, -2},
2061+
{-1, 3, 0, -1},
2062+
{1, -3, 0, 1},
2063+
{-1, -3, 1, 2},
2064+
// examples from spec#Arithmetic_operators
2065+
{5, 3, 2, -1},
2066+
{-5, 3, -1, -2},
2067+
{5, -3, -1, 2},
2068+
{-5, -3, 2, 1},
2069+
{1, 2, 1, -1},
2070+
{8, 4, 2, 0},
2071+
} {
2072+
if test.q*test.y+test.m != test.x {
2073+
t.Errorf("#%v invalid test, q*y + m != x", i)
2074+
}
2075+
x := NewInt(test.x)
2076+
y := NewInt(test.y)
2077+
m := new(Int)
2078+
q, _ := x.CeilMod(x, y, m)
2079+
if q.Cmp(NewInt(test.q)) != 0 || m.Cmp(NewInt(test.m)) != 0 {
2080+
t.Errorf("#%v got q=%v m=%v, want q=%v m=%v", i, q, m, test.q, test.m)
2081+
}
2082+
}
2083+
2084+
}

0 commit comments

Comments
 (0)