メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

新機能

math/big.Int に丸めモードを指定できる統一除算メソッドDivideを追加。

math/big

この項目の注釈は AI により生成されており、誤りを含む場合があります。
使用例のコンパイル検証: 検証済み

概要

math/big.Int に、丸めモードを指定できる統一的な整数除算メソッド
Divide が追加された。商 q と剰余 r
q = f(x/y)r = x - y*q の形で計算し、丸め方式 f
RoundingMode 型のエイリアス定数
TruncFloor
RoundCeil
いずれかで指定する。znil にすれば商の計算を省略でき、rnil にすれば剰余の計算を省略できる。

これにより、これまで既存の Div/DivMod(ユークリッド除算)や Quo/QuoRem(トランケート除算)
だけでは直接表現できなかった床除算・天井除算・最近接偶数丸め除算を、単一のAPIで直感的に書けるようになる。

導入経緯

issueは当初、big.RatFloor/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 の省略可能化: 商のみ、あるいは剰余のみが必要なケースで無駄な計算を避けられるよう、zr はそれぞれ 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 には TruncFloorRoundCeil のいずれかしか渡せない。それ以外の値を渡すと
実行時にパニックする。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)へのエイリアスとして定義されており、
FloatInt で丸めモードの語彙が共有される設計になっている。

関連リンク