math/big.Int に丸めモードを指定できる統一除算メソッドDivideを追加。
math/big
概要
math/big.Int に、丸めモードを指定できる統一的な整数除算メソッド
Divide が追加された。商 q と剰余 r を
q = f(x/y)、r = x - y*q の形で計算し、丸め方式 f を
RoundingMode 型のエイリアス定数
Trunc、Floor、
Round、Ceil の
いずれかで指定する。z を nil にすれば商の計算を省略でき、r を nil にすれば剰余の計算を省略できる。
これにより、これまで既存の Div/DivMod(ユークリッド除算)や Quo/QuoRem(トランケート除算)
だけでは直接表現できなかった床除算・天井除算・最近接偶数丸め除算を、単一のAPIで直感的に書けるようになる。
導入経緯
issueは当初、big.Rat に Floor/Ceil メソッドを追加する提案として2025年12月に投稿された。
その後の議論で「床・天井は本質的に除算の一種であり、big.Int 側に置く方が汎用的」という指摘がなされ、
Int.FloorDiv/Int.CeilDiv 系のメソッド追加へと議論が発展し、最終的には丸めモードを引数に取る
単一の Int.Divide メソッドへ設計が収束した。
議論タイムライン
- 2025年12月: issue登録。当初提案は
Rat.Floor()/Rat.Ceil()。 - 2026年2月11日: Proposal Review Meetingでactiveに移行。議論を経て
Int.FloorDiv/Int.CeilDiv系メソッドの追加も検討範囲に加わる。 - 2026年3月11日: Proposal Review Meetingで、丸めモード引数を取る単一の
Int.Divideメソッド案について合意し、likely acceptに移行。 - 2026年3月18日: コンセンサスに変化がなかったため、accepted として正式に採用。
議論のハイライト
- 設計の進化: 当初の
func (x *Rat) Floor() *Intという案は、*Ratの「レシーバに代入して返す」慣用パターンに合わないという指摘や、big.Intに直接追加する方がRatを経由せず汎用的に使えるという指摘を受け、Int.FloorDiv/Int.CeilDivを経て最終的にInt.Divideという単一の統一インターフェースに集約された。 - 命名論争:
FloorDivにするかFloorQuoにするかで @griesemer と @magical が議論した。@griesemer はGoの/に相当する演算がQuo(トランケート除算)であるため一貫性の観点からFloorQuoを主張し、@magical は「操作」を表す名前としてDiv系を主張した。最終的にメソッド名の論争は単一のDivideメソッドへの統合によって解消された。 Roundモードの追加: floor・ceil・truncの3つがあるなら、IEEE 754の標準的な4丸めモードを揃えるべきという指摘から、最近接偶数丸め(ToNearestEven)を用いるRoundモードも追加された。- 既存
RoundingMode型の再利用:math/bigにはbig.Float用のRoundingMode型がすでに存在しており、パッケージ内の一貫性のためこれをInt.Divideの丸めモード指定にもそのまま流用することになった。 z/rの省略可能化: 商のみ、あるいは剰余のみが必要なケースで無駄な計算を避けられるよう、zとrはそれぞれnilを渡して省略できる設計が採用された。
使用例
Before
package main
import (
"fmt"
"math/big"
)
// ceilDiv computes the ceiling of x/y using the existing Div method,
// which performs ceiling division when the divisor is negative.
func ceilDiv(x, y *big.Int) *big.Int {
n := new(big.Int).Neg(x)
d := new(big.Int).Neg(y)
return new(big.Int).Div(n, d)
}
func main() {
x := big.NewInt(7)
y := big.NewInt(2)
fmt.Println(ceilDiv(x, y)) // 4
}
After
package main
import (
"fmt"
"math/big"
)
func main() {
x := big.NewInt(7)
y := big.NewInt(2)
q, _ := new(big.Int).Divide(x, y, nil, big.Ceil)
fmt.Println(q) // 4
}
移行時の注意
mode には Trunc、Floor、Round、Ceil のいずれかしか渡せない。それ以外の値を渡すと
実行時にパニックする。y がゼロの場合はゼロ除算パニックが発生する点は既存の Div/Quo と同様。
実装解説
実装は
src/math/big/int.go
の Divide メソッドにある。内部的には既存の非公開ヘルパーnat.divで商・剰余の絶対値をトランケート除算
として求めたうえで、mode に応じて商・剰余を補正する(Floor/Ceilではあまりが0でなければ商を1加算し
剰余を |y| - r に置き換え、Round では剰余を2倍した値と |y| を比較して最近接側に丸める)。
Trunc/Floor/Round/Ceil の4定数は
同ファイルの const ブロック
で、big.Float が使う
RoundingMode
の値(ToZero/ToNegativeInf/ToNearestEven/ToPositiveInf)へのエイリアスとして定義されており、
Float と Int で丸めモードの語彙が共有される設計になっている。