diff --git a/api/next/76821.txt b/api/next/76821.txt new file mode 100644 index 00000000000000..d850cdb93b826e --- /dev/null +++ b/api/next/76821.txt @@ -0,0 +1,6 @@ +pkg math/big, method (*Int) FloorDiv(*Int, *Int) *Int #76821 +pkg math/big, method (*Int) FloorDivMod(*Int, *Int, *Int) (*Int, *Int) #76821 +pkg math/big, method (*Int) CeilDiv(*Int, *Int) *Int #76821 +pkg math/big, method (*Int) CeilDivMod(*Int, *Int, *Int) (*Int, *Int) #76821 +pkg math/big, method (*Rat) Floor() *Int #76821 +pkg math/big, method (*Rat) Ceil() *Int #76821 diff --git a/doc/next/6-stdlib/99-minor/math/big/76821.md b/doc/next/6-stdlib/99-minor/math/big/76821.md new file mode 100644 index 00000000000000..dd6d7c9c9bf52e --- /dev/null +++ b/doc/next/6-stdlib/99-minor/math/big/76821.md @@ -0,0 +1,4 @@ + +[Int] and [Rat] now support floor and ceiling operations: +[Int] has [Int.FloorDiv], [Int.FloorDivMod], [Int.CeilDiv] and [Int.CeilDivMod] methods. +[Rat] has [Rat.Floor] and [Rat.Ceil] methods. diff --git a/src/math/big/int.go b/src/math/big/int.go index 8eb0db6c5870ff..295559842a633d 100644 --- a/src/math/big/int.go +++ b/src/math/big/int.go @@ -1308,3 +1308,85 @@ func (z *Int) Sqrt(x *Int) *Int { z.abs = z.abs.sqrt(nil, x.abs) return z } + +// FloorDiv sets z to ⌊x/y⌋ for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// FloorDiv implements floor division (unlike Go); see [Int.FloorDivMod] for more details. +func (z *Int) FloorDiv(x, y *Int) *Int { + y0 := y // save y + if z == y || alias(z.abs, y.abs) { + y0 = new(Int).Set(y) + } + var m Int + z.DivMod(x, y, &m) + if len(m.abs) == 0 || !y0.neg { + return z + } + z.Sub(z, intOne) + return z +} + +// FloorDivMod sets z to ⌊x/y⌋ and m to x mod y +// for y != 0 and returns the pair (z, m). +// If y == 0, a division-by-zero run-time panic occurs. +// +// FloorDivMod implements floor division and modulus (unlike Go): +// +// q = ⌊x/y⌋ and +// m = x - y*q where 0 <= |m| < |y|, sgn(m) ∈ {0, sgn(y)} +// +// See [Int.QuoRem] for T-division and modulus (like Go). +func (z *Int) FloorDivMod(x, y, m *Int) (*Int, *Int) { + y0 := y // save y + if z == y || alias(z.abs, y.abs) { + y0 = new(Int).Set(y) + } + z.DivMod(x, y, m) + if len(m.abs) == 0 || !y0.neg { + return z, m + } + z.Sub(z, intOne) + m.Add(m, y0) + return z, m +} + +// CeilDiv sets z to ⌈x/y⌉ for y != 0 and returns z. +// If y == 0, a division-by-zero run-time panic occurs. +// CeilDiv implements ceiling division (unlike Go); see [Int.CeilDivMod] for more details. +func (z *Int) CeilDiv(x, y *Int) *Int { + y0 := y // save y + if z == y || alias(z.abs, y.abs) { + y0 = new(Int).Set(y) + } + var m Int + z.DivMod(x, y, &m) + if len(m.abs) == 0 || y0.neg { + return z + } + z.Add(z, intOne) + return z +} + +// CeilDivMod sets z to ⌈x/y⌉ and m to x mod y +// for y != 0 and returns the pair (z, m). +// If y == 0, a division-by-zero run-time panic occurs. +// +// CeilDivMod implements ceiling division and modulus (unlike Go): +// +// q = ⌈x/y⌉ and +// m = x - y*q where 0 <= |m| < |y|, sgn(m) ∈ {0, -sgn(y)} +// +// See [Int.QuoRem] for T-division and modulus (like Go). +func (z *Int) CeilDivMod(x, y, m *Int) (*Int, *Int) { + y0 := y // save y + if z == y || alias(z.abs, y.abs) { + y0 = new(Int).Set(y) + } + z.DivMod(x, y, m) + if len(m.abs) == 0 || y0.neg { + return z, m + } + z.Add(z, intOne) + m.Sub(m, y0) + return z, m +} diff --git a/src/math/big/int_test.go b/src/math/big/int_test.go index eb5c177be0f53a..87eb12c673c7e5 100644 --- a/src/math/big/int_test.go +++ b/src/math/big/int_test.go @@ -2010,3 +2010,73 @@ func TestFloat64(t *testing.T) { } } } + +func TestIntFloorDivMod(t *testing.T) { + for i, test := range []struct { + x, y, q, m int64 + }{ + {0, 1, 0, 0}, + {0, -1, 0, 0}, + {1, 1, 1, 0}, + {1, -1, -1, 0}, + {-1, 1, -1, 0}, + {-1, -1, 1, 0}, + {1, 3, 0, 1}, + {-1, 3, -1, 2}, + {1, -3, -1, -2}, + {-1, -3, 0, -1}, + // examples from spec#Arithmetic_operators + {5, 3, 1, 2}, + {-5, 3, -2, 1}, + {5, -3, -2, -1}, + {-5, -3, 1, -2}, + {1, 2, 0, 1}, + {8, 4, 2, 0}, + } { + if test.q*test.y+test.m != test.x { + t.Errorf("#%v invalid test, q*y + m != x", i) + } + x := NewInt(test.x) + y := NewInt(test.y) + m := new(Int) + q, _ := x.FloorDivMod(x, y, m) + if q.Cmp(NewInt(test.q)) != 0 || m.Cmp(NewInt(test.m)) != 0 { + t.Errorf("#%v got q=%v m=%v, want q=%v m=%v", i, q, m, test.q, test.m) + } + } +} + +func TestIntCeilDivMod(t *testing.T) { + for i, test := range []struct { + x, y, q, m int64 + }{ + {0, 1, 0, 0}, + {0, -1, 0, 0}, + {1, 1, 1, 0}, + {1, -1, -1, 0}, + {-1, 1, -1, 0}, + {-1, -1, 1, 0}, + {1, 3, 1, -2}, + {-1, 3, 0, -1}, + {1, -3, 0, 1}, + {-1, -3, 1, 2}, + // examples from spec#Arithmetic_operators + {5, 3, 2, -1}, + {-5, 3, -1, -2}, + {5, -3, -1, 2}, + {-5, -3, 2, 1}, + {1, 2, 1, -1}, + {8, 4, 2, 0}, + } { + if test.q*test.y+test.m != test.x { + t.Errorf("#%v invalid test, q*y + m != x", i) + } + x := NewInt(test.x) + y := NewInt(test.y) + m := new(Int) + q, _ := x.CeilDivMod(x, y, m) + if q.Cmp(NewInt(test.q)) != 0 || m.Cmp(NewInt(test.m)) != 0 { + t.Errorf("#%v got q=%v m=%v, want q=%v m=%v", i, q, m, test.q, test.m) + } + } +} diff --git a/src/math/big/rat.go b/src/math/big/rat.go index c7f79a56661b12..4439b41e91e3f7 100644 --- a/src/math/big/rat.go +++ b/src/math/big/rat.go @@ -559,3 +559,13 @@ func (z *Rat) Quo(x, y *Rat) *Rat { z.a.neg = a.neg != b.neg return z.norm() } + +// Floor returns ⌊x⌋ (the floor of x) as a new [Int]. +func (x *Rat) Floor() *Int { + return new(Int).FloorDiv(x.Num(), x.Denom()) +} + +// Ceil returns ⌈x⌉ (the ceiling of x) as a new [Int]. +func (x *Rat) Ceil() *Int { + return new(Int).CeilDiv(x.Num(), x.Denom()) +}