Skip to content

Commit e8ca293

Browse files
committed
math/big: add floor and ceiling operations to Int and Rat
Adds methods FloorDiv, FloorDivMod, CeilDiv and CeilDivMod to Int. Adds methods Floor and Ceil to Rat. Fixes #76821
1 parent 1b291b7 commit e8ca293

File tree

5 files changed

+172
-0
lines changed

5 files changed

+172
-0
lines changed

api/next/76821.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pkg math/big, method (*Int) FloorDiv(*Int, *Int) *Int #76821
2+
pkg math/big, method (*Int) FloorDivMod(*Int, *Int, *Int) (*Int, *Int) #76821
3+
pkg math/big, method (*Int) CeilDiv(*Int, *Int) *Int #76821
4+
pkg math/big, method (*Int) CeilDivMod(*Int, *Int, *Int) (*Int, *Int) #76821
5+
pkg math/big, method (*Rat) Floor() *Int #76821
6+
pkg math/big, method (*Rat) Ceil() *Int #76821
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<!-- go.dev/issue/76821 -->
2+
[Int] and [Rat] now support floor and ceiling operations:
3+
[Int] has [Int.FloorDiv], [Int.FloorDivMod], [Int.CeilDiv] and [Int.CeilDivMod] methods.
4+
[Rat] has [Rat.Floor] and [Rat.Ceil] methods.

src/math/big/int.go

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

src/math/big/int_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,3 +2010,73 @@ func TestFloat64(t *testing.T) {
20102010
}
20112011
}
20122012
}
2013+
2014+
func TestIntFloorDivMod(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.FloorDivMod(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+
func TestIntCeilDivMod(t *testing.T) {
2050+
for i, test := range []struct {
2051+
x, y, q, m int64
2052+
}{
2053+
{0, 1, 0, 0},
2054+
{0, -1, 0, 0},
2055+
{1, 1, 1, 0},
2056+
{1, -1, -1, 0},
2057+
{-1, 1, -1, 0},
2058+
{-1, -1, 1, 0},
2059+
{1, 3, 1, -2},
2060+
{-1, 3, 0, -1},
2061+
{1, -3, 0, 1},
2062+
{-1, -3, 1, 2},
2063+
// examples from spec#Arithmetic_operators
2064+
{5, 3, 2, -1},
2065+
{-5, 3, -1, -2},
2066+
{5, -3, -1, 2},
2067+
{-5, -3, 2, 1},
2068+
{1, 2, 1, -1},
2069+
{8, 4, 2, 0},
2070+
} {
2071+
if test.q*test.y+test.m != test.x {
2072+
t.Errorf("#%v invalid test, q*y + m != x", i)
2073+
}
2074+
x := NewInt(test.x)
2075+
y := NewInt(test.y)
2076+
m := new(Int)
2077+
q, _ := x.CeilDivMod(x, y, m)
2078+
if q.Cmp(NewInt(test.q)) != 0 || m.Cmp(NewInt(test.m)) != 0 {
2079+
t.Errorf("#%v got q=%v m=%v, want q=%v m=%v", i, q, m, test.q, test.m)
2080+
}
2081+
}
2082+
}

src/math/big/rat.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,3 +559,13 @@ func (z *Rat) Quo(x, y *Rat) *Rat {
559559
z.a.neg = a.neg != b.neg
560560
return z.norm()
561561
}
562+
563+
// Floor returns ⌊x⌋ (the floor of x) as a new [Int].
564+
func (x *Rat) Floor() *Int {
565+
return new(Int).FloorDiv(x.Num(), x.Denom())
566+
}
567+
568+
// Ceil returns ⌈x⌉ (the ceiling of x) as a new [Int].
569+
func (x *Rat) Ceil() *Int {
570+
return new(Int).CeilDiv(x.Num(), x.Denom())
571+
}

0 commit comments

Comments
 (0)