Go言語 big.Int.Quo()プログラミング例:負の数・ゼロ除算の扱い方

2025-06-01

big.Int.Quo()の基本的な使い方

Quo()メソッドのシグネチャは以下のようになっています。

func (z *Int) Quo(x, y *Int) *Int
  • y: 除数(割る数)を表すInt型のポインタです。
  • x: 被除数(割られる数)を表すInt型のポインタです。
  • z: 結果が格納されるInt型のポインタです。通常、このzはメソッドを呼び出すレシーバとして指定されます。

このメソッドは、xyで割った商を計算し、その結果をzに格納します。そして、z自身を返します。

重要な点

  • 切り捨て
    Quo()は、数学的な商(x/y)をゼロ方向へ切り捨てた整数部分を返します。
    • 例1: Quo(10, 3)は3を返します。(10/3=3.33...→3)
    • 例2: Quo(-10, 3)は-3を返します。(−10/3=−3.33...→−3)
    • 例3: Quo(10, -3)は-3を返します。(10/−3=−3.33...→−3)
    • 例4: Quo(-10, -3)は3を返します。(−10/−3=3.33...→3)
  • ゼロ除算
    もしyが0の場合、Quo()はパニック(panic)を発生させます。

使用例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	// 新しいbig.Intオブジェクトを初期化
	a := new(big.Int)
	b := new(big.Int)
	c := new(big.Int) // 商を格納するInt

	// 値を設定
	a.SetString("12345678901234567890", 10) // 非常に大きな数
	b.SetString("7", 10)                  // 割る数

	// a を b で割った商を c に格納
	c.Quo(a, b)

	fmt.Printf("a = %s\n", a.String())
	fmt.Printf("b = %s\n", b.String())
	fmt.Printf("a / b (商) = %s\n", c.String())

	// 負の数の例
	x := big.NewInt(-10)
	y := big.NewInt(3)
	res := new(big.Int)
	res.Quo(x, y)
	fmt.Printf("%s / %s = %s\n", x.String(), y.String(), res.String()) // 出力: -10 / 3 = -3

	x = big.NewInt(10)
	y = big.NewInt(-3)
	res.Quo(x, y)
	fmt.Printf("%s / %s = %s\n", x.String(), y.String(), res.String()) // 出力: 10 / -3 = -3
}

math/bigパッケージには、Div()というメソッドも存在します。Go 1.9以降では、Div()Quo()のエイリアス(別名)になっています。したがって、機能的には全く同じです。以前のバージョンでは、Div()は商と剰余を同時に返すものでしたが、現在ではQuo()Rem()(剰余)が独立したメソッドとして推奨されています。



ゼロ除算 (Panic: division by zero)

これは最も頻繁に発生し、かつ最も注意すべきエラーです。 Quo()メソッドは、除数(y)がゼロの場合に**パニック(panic)**を発生させます。これは、通常のGoの組み込み整数型でのゼロ除算と同様の動作です。

エラーメッセージ例

panic: division by zero

原因
Quo(x, y)y(除数)がbig.NewInt(0)、またはSetInt64(0)などでゼロに設定されている場合。

トラブルシューティング

  1. 除数のゼロチェック
    Quo()を呼び出す前に、除数がゼロでないことを必ず確認してください。

    package main
    
    import (
    	"fmt"
    	"math/big"
    )
    
    func main() {
    	a := big.NewInt(100)
    	b := big.NewInt(0) // ゼロ除数
    
    	res := new(big.Int)
    
    	// ゼロ除算チェック
    	if b.Cmp(big.NewInt(0)) == 0 { // bが0と比較して等しい場合
    		fmt.Println("Error: Division by zero is not allowed.")
    		// エラーハンドリング(例: ログ出力、エラー値の返却、デフォルト値の設定など)
    		return
    	}
    
    	res.Quo(a, b)
    	fmt.Printf("%s / %s = %s\n", a.String(), b.String(), res.String())
    }
    
    • b.Cmp(big.NewInt(0)) == 0 は、bがゼロと等しいかどうかをチェックする慣用的な方法です。
  2. 入力値の検証
    外部からの入力値(ユーザー入力、ファイル、ネットワークなど)をbig.Intに変換して除数として使用する場合、その値がゼロでないことを確実に検証するロジックを組み込む必要があります。

ポインタのnil値 (Nil Pointer Dereference)

big.Intのメソッドはポインタレシーバ(*Int)を取るため、nilポインタに対してメソッドを呼び出すと、nilポインタデリファレンスが発生します。

エラーメッセージ例

panic: runtime error: invalid memory address or nil pointer dereference

原因

  • 被除数xまたは除数ynilの場合。

    package main
    
    import (
    	"math/big"
    )
    
    func main() {
    	var a *big.Int // nil
    	b := big.NewInt(5)
    	res := new(big.Int)
    
    	// aがnilなので、ここでpanic
    	res.Quo(a, b) // a が nil なので、エラー
    }
    
  • Quo()の結果を格納するレシーバznilの場合。

トラブルシューティング

  1. new(big.Int)またはbig.NewInt()で初期化
    big.Int型の変数は、使用する前に必ずnew(big.Int)またはbig.NewInt()で初期化してください。

    package main
    
    import (
    	"fmt"
    	"math/big"
    )
    
    func main() {
    	a := new(big.Int) // 正しい初期化
    	a.SetString("100", 10)
    
    	b := big.NewInt(5) // 正しい初期化
    
    	res := new(big.Int) // 結果を格納する変数も初期化が必要
    
    	res.Quo(a, b)
    	fmt.Printf("%s / %s = %s\n", a.String(), b.String(), res.String())
    }
    

数値の基数(Radix)の誤解

SetString()などを使用してbig.Intを初期化する際、基数(radix)の指定を誤ると、意図しない数値が設定される可能性があります。これは直接Quo()のエラーを引き起こすわけではありませんが、計算結果が期待と異なる原因になります。


big.NewInt().SetString("10", 2)は、文字列"10"をバイナリ(2進数)として解釈するため、値は2になります。意図して10進数の10を設定したい場合は、big.NewInt().SetString("10", 10)とする必要があります。

トラブルシューティング

  • SetString()を使用する際は、必ず正しい基数(通常は10進数の10)を指定してください。

切り捨ての動作の誤解

Quo()ゼロ方向への切り捨てを行います。これは、通常のプログラミング言語における整数除算の動作とは異なる場合があります(特に負の数の場合)。


  • Pythonなどでは-10 // 3-4(負の無限大方向への切り捨て)になるため、他の言語からの移植時には注意が必要です。
  • Quo(-10, 3) -> -3 (-3.33... のゼロ方向への切り捨て)
  • Quo(10, 3) -> 3 (正しい)

トラブルシューティング

  • Quo()の動作を完全に理解し、必要であれば結果を調整するロジック(例: 負の無限大方向への切り捨てが必要な場合は、Rem()と組み合わせて調整する)を検討してください。

巨大な数値によるパフォーマンス問題

big.Intは任意精度であるため、非常に巨大な数値を扱うことができます。しかし、その分、計算には時間とメモリを消費します。特に、除算は加算や乗算に比べて計算コストが高い操作です。

トラブルシューティング

  • 数値の範囲確認
    扱う数値がint64などの組み込み型で収まるのであれば、big.Intではなく組み込み型を使用する方がパフォーマンスは格段に良くなります。本当にbig.Intが必要か再確認することも重要です。
  • アルゴリズムの最適化
    そもそもbig.Intを多数使用するようなアルゴリズム自体を見直すことで、計算回数を減らすことができないか検討してください。
  • プロファイリング
    処理が遅いと感じる場合は、Goのプロファイリングツール(pprof)を使用して、big.Intの操作がボトルネックになっていないか確認してください。


例1: 基本的な除算

最も基本的なbig.Int.Quo()の使用例です。

package main

import (
	"fmt"
	"math/big" // big.Int型を使うためにmath/bigパッケージをインポート
)

func main() {
	// big.Int型の変数を宣言し、初期化します。
	// new(big.Int)で新しいIntオブジェクトのポインタを作成します。
	numerator := new(big.Int)   // 被除数(割られる数)
	denominator := new(big.Int) // 除数(割る数)
	quotient := new(big.Int)    // 商(結果を格納する変数)

	// SetString()メソッドを使って、文字列から大きな数値をセットします。
	// 第2引数は基数(radix)で、10進数の場合は10を指定します。
	numerator.SetString("123456789012345678901234567890", 10) // 非常に大きな数
	denominator.SetString("7654321", 10)                        // 割る数

	fmt.Println("--- 基本的な除算 ---")
	fmt.Printf("被除数 (numerator): %s\n", numerator.String())
	fmt.Printf("除数 (denominator): %s\n", denominator.String())

	// Quo()メソッドを使って除算を行います。
	// quotient = numerator / denominator
	// メソッドはレシーバ (quotient) 自身を返します。
	quotient.Quo(numerator, denominator)

	fmt.Printf("商 (quotient): %s\n", quotient.String())
	// 期待される出力: 1612903225829633596489397
}

解説

  • String(): big.Intの値を文字列として返します。出力やデバッグに便利です。
  • Quo(x, y *Int): xyで割り、その商をレシーバ(この例ではquotient)に格納します。
  • SetString(s string, base int): 文字列sbase(基数)で指定された数値として解釈し、big.Intに設定します。ここでは10進数で設定しています。
  • new(big.Int): 新しいbig.Intオブジェクトをメモリ上に確保し、そのポインタを返します。
  • import "math/big": big.Int型を使用するために必須です。

例2: 負の数を含む除算と切り捨ての動作

Quo()ゼロ方向への切り捨てを行います。これは負の数の場合に特に重要です。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	fmt.Println("\n--- 負の数を含む除算 ---")

	// 正の被除数、正の除数
	a := big.NewInt(10)
	b := big.NewInt(3)
	res := new(big.Int)
	res.Quo(a, b)
	fmt.Printf("%s / %s = %s (10 / 3 = 3.33... -> 3)\n", a.String(), b.String(), res.String())

	// 負の被除数、正の除数
	a.SetInt64(-10) // big.Intの値を変更
	b.SetInt64(3)
	res.Quo(a, b)
	fmt.Printf("%s / %s = %s (-10 / 3 = -3.33... -> -3)\n", a.String(), b.String(), res.String())

	// 正の被除数、負の除数
	a.SetInt64(10)
	b.SetInt64(-3)
	res.Quo(a, b)
	fmt.Printf("%s / %s = %s (10 / -3 = -3.33... -> -3)\n", a.String(), b.String(), res.String())

	// 負の被除数、負の除数
	a.SetInt64(-10)
	b.SetInt64(-3)
	res.Quo(a, b)
	fmt.Printf("%s / %s = %s (-10 / -3 = 3.33... -> 3)\n", a.String(), b.String(), res.String())
}

解説

  • ゼロ方向への切り捨て
    結果が正の場合も負の場合も、小数点以下はゼロに近い方に切り捨てられます。これが一般的なプログラミング言語の整数除算の動作と異なる場合がある(例: Pythonの//演算子は負の無限大方向へ切り捨てる)点に注意してください。
  • SetInt64(int64): 既存のbig.Intオブジェクトの値をint64から設定します。
  • big.NewInt(int64): int64の値からbig.Intを直接作成する便利な関数です。

例3: ゼロ除算のハンドリング

Quo()は除数がゼロの場合にパニックを発生させます。これを回避するためのチェックを含めることが重要です。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	fmt.Println("\n--- ゼロ除算のハンドリング ---")

	a := big.NewInt(100)
	b := big.NewInt(0) // 除数をゼロに設定
	res := new(big.Int)

	// 除数がゼロでないことを確認
	// Cmp(y *Int) int: yとレシーバを比較し、レシーバ < y なら-1、レシーバ == y なら0、レシーバ > y なら1を返します。
	if b.Cmp(big.NewInt(0)) == 0 {
		fmt.Printf("エラー: ゼロによる除算は許可されていません (%s / %s)\n", a.String(), b.String())
		// ここで適切なエラーハンドリングを行います。
		// 例えば、エラー値を返す、ログに記録する、処理を中断する、など。
		return // main関数を終了
	}

	// ゼロ除算でない場合のみQuoを呼び出す
	res.Quo(a, b)
	fmt.Printf("%s / %s = %s\n", a.String(), b.String(), res.String())
}

解説

  • エラーハンドリング
    実際のアプリケーションでは、単にreturnするだけでなく、エラーメッセージをユーザーに表示したり、エラーログに記録したり、特定のデフォルト値を返したりするなど、より堅牢なエラーハンドリングを行うべきです。
  • b.Cmp(big.NewInt(0)) == 0: これはbがゼロと等しいかどうかをチェックする標準的な方法です。Cmpメソッドは2つのbig.Intを比較します。

例4: 商と剰余を同時に計算する (QuoRem)

商だけでなく剰余も必要な場合は、QuoRem()メソッドを使用すると効率的です。これはQuo()Rem()を別々に呼び出すよりも優れています。

package main

import (
	"fmt"
	"math/big"
)

func main() {
	fmt.Println("\n--- 商と剰余を同時に計算 ---")

	numerator := big.NewInt(17)
	denominator := big.NewInt(5)
	quotient := new(big.Int)
	remainder := new(big.Int)

	// QuoRem(quotient, remainder, x, y)
	// quotient = x / y (商)
	// remainder = x % y (剰余)
	quotient.QuoRem(numerator, denominator, quotient, remainder) // QuoRemの第3引数と第4引数は結果を格納するポインタ

	fmt.Printf("被除数: %s\n", numerator.String())
	fmt.Printf("除数: %s\n", denominator.String())
	fmt.Printf("商: %s\n", quotient.String())      // 17 / 5 = 3
	fmt.Printf("剰余: %s\n", remainder.String()) // 17 % 5 = 2

	// 負の数の例
	numNeg := big.NewInt(-17)
	denPos := big.NewInt(5)
	quotientNeg := new(big.Int)
	remainderNeg := new(big.Int)

	quotientNeg.QuoRem(numNeg, denPos, quotientNeg, remainderNeg)
	fmt.Printf("%s / %s -> 商: %s, 剰余: %s\n", numNeg.String(), denPos.String(), quotientNeg.String(), remainderNeg.String())
	// 結果: -17 / 5 -> 商: -3, 剰余: -2 (ゼロ方向への切り捨て)
}
  • 剰余の符号
    剰余の符号は、被除数(x)の符号と同じになります。これはbig.Int.Rem()の仕様です。例えば、-17 / 5の商は-3で、剰余は(-17) - (-3 * 5) = -17 - (-15) = -2となります。
  • QuoRem(z, r, x, y *Int): xyで割った商をzに、剰余をrに格納します。


主に以下の3つの観点から代替手段や関連メソッドを説明します。

  1. 商と剰余を同時に取得する場合
    QuoRem()
  2. 異なる切り捨て規則の商を計算する場合
    Div()や手動での調整
  3. 商の計算が不要で剰余のみ必要な場合
    Rem()
  4. 正確な割り算(小数部分を扱う場合)
    big.Ratの使用

商と剰余を同時に取得する: big.Int.QuoRem()

QuoRem()は、商(Quotient)と剰余(Remainder)を一度の呼び出しで計算できる非常に便利なメソッドです。これは、Quo()Rem()を別々に呼び出すよりも効率的です。

メソッドシグネチャ

func (z *Int) QuoRem(x, y, q, r *Int) (*Int, *Int)
  • r: 剰余を格納するIntポインタ
  • q: 商を格納するIntポインタ
  • y: 除数
  • x: 被除数
  • z: 結果の商を格納するIntポインタ (戻り値の商と同じ)

振る舞い

  • y がゼロの場合、パニックが発生します。
  • r には x % y の剰余が格納されます。剰余の符号は x の符号と同じになります。
  • q には x / y の商がゼロ方向へ切り捨てられて格納されます。

使用例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	fmt.Println("--- QuoRem() を使用した商と剰余の同時取得 ---")

	a := big.NewInt(17)
	b := big.NewInt(5)
	
	// 商と剰余を格納する変数
	quotient := new(big.Int)
	remainder := new(big.Int)

	// QuoRem メソッドの呼び出し
	// 第一引数は商のレシーバ、第二引数は剰余のレシーバとして使うこともできる
	// この場合、quotient と remainder は同じオブジェクトを指すことになる
	quotient.QuoRem(a, b, quotient, remainder) // a / b の商を quotient に、剰余を remainder に格納

	fmt.Printf("%s を %s で割ると:\n", a.String(), b.String())
	fmt.Printf("商 (quotient): %s\n", quotient.String())      // 3
	fmt.Printf("剰余 (remainder): %s\n", remainder.String()) // 2

	// 負の数の例
	aNeg := big.NewInt(-17)
	bPos := big.NewInt(5)
	
	qNeg := new(big.Int)
	rNeg := new(big.Int)

	qNeg.QuoRem(aNeg, bPos, qNeg, rNeg)
	fmt.Printf("%s を %s で割ると:\n", aNeg.String(), bPos.String())
	fmt.Printf("商 (quotient): %s\n", qNeg.String()) // -3 (ゼロ方向へ切り捨て)
	fmt.Printf("剰余 (remainder): %s\n", rNeg.String()) // -2 (被除数と同じ符号)
}

いつ使うか

  • パフォーマンスが重要な場合、Quo()Rem()を個別に呼び出すよりも効率的です。
  • 除算の結果と同時に剰余も必要になる場合。

異なる切り捨て規則の商を計算する

big.Int.Quo()はゼロ方向への切り捨て(truncation)を行います。しかし、プログラミングでは他の切り捨て規則(例: 負の無限大方向への切り捨て、四捨五入)が必要になることがあります。math/bigパッケージにはこれらの直接的なメソッドはありませんが、QuoRem()Rem()を使って手動で実装できます。

a. 負の無限大方向への切り捨て(Floor division)

多くの言語(Pythonなど)の整数除算は、負の無限大方向へ切り捨てます。

実装例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	fmt.Println("\n--- 負の無限大方向への切り捨て (Floor Division) ---")

	x := big.NewInt(-10)
	y := big.NewInt(3)
	
	q := new(big.Int)
	r := new(big.Int)

	q.QuoRem(x, y, q, r) // まずゼロ方向への切り捨てで計算

	// 剰余が0でなく、かつ被除数と除数の符号が異なる場合、商から1を引く
	// 例: -10 / 3 = -3.33... (QuoRemは-3)。剰余が -10 % 3 = -1。
	// この場合、商を-4にしたいので、-3から1を引く。
	if r.Cmp(big.NewInt(0)) != 0 && ((x.Sign() > 0 && y.Sign() < 0) || (x.Sign() < 0 && y.Sign() > 0)) {
		q.Sub(q, big.NewInt(1)) // 商から1を引く
	}

	fmt.Printf("Floor division of %s by %s = %s\n", x.String(), y.String(), q.String()) // -10 / 3 -> -4
	
	x = big.NewInt(10)
	y = big.NewInt(-3)
	q.QuoRem(x, y, q, r)
	if r.Cmp(big.NewInt(0)) != 0 && ((x.Sign() > 0 && y.Sign() < 0) || (x.Sign() < 0 && y.Sign() > 0)) {
		q.Sub(q, big.NewInt(1))
	}
	fmt.Printf("Floor division of %s by %s = %s\n", x.String(), y.String(), q.String()) // 10 / -3 -> -4
}

解説

  • このロジックは、ゼロ方向への切り捨ての結果(q)が、負の無限大方向への切り捨てと異なる場合に調整を加えます。具体的には、剰余がゼロでなく、かつ被除数と除数の符号が異なる場合(つまり結果が負の小数になる場合)、商から1を引きます。
  • x.Sign(): big.Intの符号を返します。正なら1、負なら-1、ゼロなら0。

b. 四捨五入(Round to nearest integer)

最も近い整数への丸めが必要な場合です。0.5の扱いは状況によります(例: 偶数への丸め)。

実装例(単純な四捨五入、0.5は正の無限大方向へ丸める)

package main

import (
	"fmt"
	"math/big"
)

func main() {
	fmt.Println("\n--- 四捨五入 (Round to nearest integer) ---")

	x := big.NewInt(10)
	y := big.NewInt(3) // 10 / 3 = 3.33... -> 3

	q := new(big.Int)
	r := new(big.Int)
	halfY := new(big.Int).Div(new(big.Int).Abs(y), big.NewInt(2)) // |y|/2

	q.QuoRem(x, y, q, r)

	// 剰余の絶対値が除数の半分以上であれば丸める
	// rの符号はxの符号と同じなので、Abs()で絶対値を取る
	if new(big.Int).Abs(r).Cmp(halfY) >= 0 {
		if x.Sign() > 0 { // xが正の場合
			q.Add(q, big.NewInt(1))
		} else if x.Sign() < 0 { // xが負の場合
			q.Sub(q, big.NewInt(1))
		}
	}
	fmt.Printf("%s / %s (四捨五入) = %s\n", x.String(), y.String(), q.String()) // 3

	x.SetInt64(11) // 11 / 3 = 3.66... -> 4
	q.QuoRem(x, y, q, r)
	if new(big.Int).Abs(r).Cmp(halfY) >= 0 {
		if x.Sign() > 0 {
			q.Add(q, big.NewInt(1))
		} else if x.Sign() < 0 {
			q.Sub(q, big.NewInt(1))
		}
	}
	fmt.Printf("%s / %s (四捨五入) = %s\n", x.String(), y.String(), q.String()) // 4

	x.SetInt64(-11) // -11 / 3 = -3.66... -> -4
	q.QuoRem(x, y, q, r)
	if new(big.Int).Abs(r).Cmp(halfY) >= 0 {
		if x.Sign() > 0 {
			q.Add(q, big.NewInt(1))
		} else if x.Sign() < 0 {
			q.Sub(q, big.NewInt(1))
		}
	}
	fmt.Printf("%s / %s (四捨五入) = %s\n", x.String(), y.String(), q.String()) // -4

	x.SetInt64(7) // 7 / 2 = 3.5 -> 4 (0.5は正の無限大方向へ丸める例)
	y.SetInt64(2)
	halfY = new(big.Int).Div(new(big.Int).Abs(y), big.NewInt(2))

	q.QuoRem(x, y, q, r)
	if new(big.Int).Abs(r).Cmp(halfY) >= 0 {
		if x.Sign() > 0 {
			q.Add(q, big.NewInt(1))
		} else if x.Sign() < 0 {
			q.Sub(q, big.NewInt(1))
		}
	}
	fmt.Printf("%s / %s (四捨五入) = %s\n", x.String(), y.String(), q.String()) // 4
}

解説

  • 剰余の絶対値が除数の半分以上の場合に、商を調整します。符号に応じて加算または減算を行います。
  • halfY: 除数の絶対値の半分を計算します。
  • Abs(): big.Intの絶対値を計算します。

剰余のみが必要な場合: big.Int.Rem()

商は不要で、剰余(x % y)のみが必要な場合は、Rem()メソッドを使用します。

メソッドシグネチャ

func (z *Int) Rem(x, y *Int) *Int
  • y: 除数
  • x: 被除数
  • z: 結果の剰余を格納するIntポインタ

振る舞い

  • y がゼロの場合、パニックが発生します。
  • z には x % y の剰余が格納されます。剰余の符号は x の符号と同じになります。

使用例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	fmt.Println("\n--- Rem() を使用した剰余のみの取得 ---")

	a := big.NewInt(17)
	b := big.NewInt(5)
	remainder := new(big.Int)

	remainder.Rem(a, b)
	fmt.Printf("%s %% %s = %s\n", a.String(), b.String(), remainder.String()) // 2

	a.SetInt64(-17)
	b.SetInt64(5)
	remainder.Rem(a, b)
	fmt.Printf("%s %% %s = %s\n", a.String(), b.String(), remainder.String()) // -2 (被除数と同じ符号)
}

いつ使うか

  • 数値の偶奇判定、巡回処理、ハッシュ計算などで、剰余のみが重要な場合。

正確な割り算(小数部分を扱う場合): big.Ratの使用

big.Intは整数のみを扱いますが、もし除算の結果が正確な分数や小数で表現される必要がある場合(例: 10 / 3 = 3.33...ではなく10/3という分数自体を扱いたい場合)、math/bigパッケージのbig.Rat型が適しています。big.Ratは任意精度の有理数(分数)を扱います。

使用例

package main

import (
	"fmt"
	"math/big"
)

func main() {
	fmt.Println("\n--- big.Rat を使用した正確な分数計算 ---")

	// big.Int を作成
	numeratorInt := big.NewInt(10)
	denominatorInt := big.NewInt(3)

	// big.Rat を作成し、numeratorInt / denominatorInt で初期化
	// SetFrac(num, den *Int) *Rat
	fraction := new(big.Rat).SetFrac(numeratorInt, denominatorInt)

	fmt.Printf("分数表現: %s\n", fraction.String()) // 10/3

	// 小数点以下も表現する場合
	// FloatString(prec int) string: 指定された精度で浮動小数点数として文字列化
	fmt.Printf("小数点表現 (精度5): %s\n", fraction.FloatString(5)) // 3.33333

	// 他の big.Rat との除算
	otherFraction := new(big.Rat).SetFrac(big.NewInt(2), big.NewInt(1)) // 2/1 = 2
	resultFraction := new(big.Rat).Quo(fraction, otherFraction) // (10/3) / 2 = 5/3

	fmt.Printf("(%s) / (%s) = %s\n", fraction.String(), otherFraction.String(), resultFraction.String())
	fmt.Printf("結果の小数点表現 (精度5): %s\n", resultFraction.FloatString(5)) // 1.66667
}

解説

  • Quo(x, y *Rat): xyで割った結果をbig.Ratとして返します。
  • FloatString(prec int): 指定された精度precで浮動小数点数として文字列を返します。
  • String(): 分数形式で文字列を返します(例: "10/3")。
  • SetFrac(num, den *Int): num / denの形式でbig.Ratを設定します。
  • big.Rat: 有理数を表す型で、分子と分母をbig.Intで内部的に保持します。
  • 金融計算、科学計算、単位変換など、丸め誤差を最小限に抑え、正確な分数表現が必要な場合。