Big库处理大数字

在日常的开发过程中,不可避免的需要使用到超过变量类型的数值计算,在 go 语言中,int64 类型的变量的储存范围是 -9223372036854775808 ~ 9223372036854775807,当我们需要计算的数值大于这个范围之后,计算出的结果就会出错,这时候就需要使用到 go 语言中专门为大数计算而存在的标准库:math/big 包里面的内容。

比如

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

package main

import (
	"fmt"
	"math"
)

func main() {
	a := math.MaxInt64
	b := math.MaxInt64
	c := a + b
	fmt.Printf("%d + %d = %d", a, b, c)
}

https://blog.meowrain.cn/api/i/2025/02/09/NeyDZZ1739103367221703865.avif

可以看出两个最大值的相加结果异常,这是因为两个最大值相加的结果超出了 int64 能够存储的范围。

math/big

如果需要进行上面这样的大数计算,可以使用 go 语言自带的 math/big 包进行计算,big 包中包含了整形 int 和浮点型 float 的大数计算。

https://blog.meowrain.cn/api/i/2025/02/09/ZvdgEs1739103414865724857.avif

https://blog.meowrain.cn/api/i/2025/02/09/9x1Erq1739103545091491536.avif

https://blog.meowrain.cn/api/i/2025/02/09/1484LS1739103553901951808.avif

https://blog.meowrain.cn/api/i/2025/02/09/y3gXfL1739103567673475304.avif

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import (
	"fmt"
	"math"
	"math/big"
)

func main() {
	a := big.NewInt(math.MaxInt64)
	b := big.NewInt(math.MaxInt64)
	result := a.Add(a, b)
	fmt.Println(result)

}

这样就可以相加了!

https://blog.meowrain.cn/api/i/2025/02/09/tGGoHz1739103516966595557.avif

从字符串生成超大数字

https://blog.meowrain.cn/api/i/2025/02/09/FIxS7B1739103630071679187.avif

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import (
	"fmt"
	"math/big"
)

func main() {
	a := big.NewInt(1)
	big1, ok := a.SetString("23431241234123412341324123412341234123434123432431241234132412341234123431241234", 10)
	if !ok {
		fmt.Println("设置失败")
	}
	fmt.Println(big1)

}

https://blog.meowrain.cn/api/i/2025/02/09/U95uV51739103730238428511.avif

浮点数

https://blog.meowrain.cn/api/i/2025/02/09/nV0CJQ1739103768696488716.avif

大有理数 big.Rat 类型

大有理数的 big.Rat 类型

big.Rat 是Go语言 math/big 包中的一个类型,用于表示任意精度的有理数(即分数形式的数,如 $\frac{a}{b}$,其中 $a$ 是分子,$b$ 是非零分母)。以下从多个方面对其进行介绍:

1. 创建 big.Rat 对象

  • 使用 big.NewRat 函数: 该函数接受两个 int64 类型的参数,分别为分子和分母,返回一个指向 big.Rat 对象的指针。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import (
    "fmt"
    "math/big"
)

func main() {
    rat := big.NewRat(3, 4)
    fmt.Println(rat)
}

此代码创建了有理数 $\frac{3}{4}$ 并输出。

  • 使用 SetFrac 方法: 该方法可用于设置 big.Rat 对象的分子和分母,接受两个 big.Int 类型的参数。这在处理大整数作为分子或分母时非常有用。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import (
    "fmt"
    "math/big"
)

func main() {
    num := big.NewInt(12345678901234567890)
    den := big.NewInt(98765432109876543210)
    rat := new(big.Rat).SetFrac(num, den)
    fmt.Println(rat)
}

2. 约分

big.Rat 对象在创建时会自动进行约分。例如,创建 $\frac{6}{8}$ 时,实际存储的是约分后的 $\frac{3}{4}$。这确保了有理数以最简形式表示,方便后续计算和比较。

3. 基本运算

  • 加法:使用 Add 方法,将两个 big.Rat 对象相加。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import (
    "fmt"
    "math/big"
)

func main() {
    rat1 := big.NewRat(1, 2)
    rat2 := big.NewRat(1, 3)
    result := new(big.Rat).Add(rat1, rat2)
    fmt.Println(result)
}

这里计算 $\frac{1}{2} + \frac{1}{3} = \frac{5}{6}$。

  • 减法:通过 Sub 方法实现。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import (
    "fmt"
    "math/big"
)

func main() {
    rat1 := big.NewRat(3, 4)
    rat2 := big.NewRat(1, 4)
    result := new(big.Rat).Sub(rat1, rat2)
    fmt.Println(result)
}

即 $\frac{3}{4}-\frac{1}{4}=\frac{2}{4}=\frac{1}{2}$(自动约分)。

  • 乘法:使用 Mul 方法。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import (
    "fmt"
    "math/big"
)

func main() {
    rat1 := big.NewRat(2, 3)
    rat2 := big.NewRat(3, 5)
    result := new(big.Rat).Mul(rat1, rat2)
    fmt.Println(result)
}

计算 $\frac{2}{3}×\frac{3}{5}=\frac{2}{5}$。

  • 除法:通过 Quo 方法进行。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import (
    "fmt"
    "math/big"
)

func main() {
    rat1 := big.NewRat(4, 5)
    rat2 := big.NewRat(2, 3)
    result := new(big.Rat).Quo(rat1, rat2)
    fmt.Println(result)
}

即 $\frac{4}{5}÷\frac{2}{3}=\frac{4}{5}×\frac{3}{2}=\frac{6}{5}$。

4. 比较

  • Cmp 方法:用于比较两个 big.Rat 对象的大小。返回值为 -1(表示第一个数小于第二个数)、0(表示两个数相等)或 1(表示第一个数大于第二个数)。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
    "fmt"
    "math/big"
)

func main() {
    rat1 := big.NewRat(1, 2)
    rat2 := big.NewRat(3, 4)
    cmp := rat1.Cmp(rat2)
    if cmp == -1 {
        fmt.Println("rat1 小于 rat2")
    } else if cmp == 0 {
        fmt.Println("rat1 等于 rat2")
    } else {
        fmt.Println("rat1 大于 rat2")
    }
}

5. 转换

  • 转换为浮点数Float64 方法将 big.Rat 对象转换为 float64 类型的值。需要注意的是,由于 float64 精度有限,可能会丢失精度。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import (
    "fmt"
    "math/big"
)

func main() {
    rat := big.NewRat(1, 3)
    f := rat.Float64()
    fmt.Printf("%.10f\n", f)
}

这里将 $\frac{1}{3}$ 转换为 float64,输出结果会有一定精度损失。

  • 转换为字符串String 方法将 big.Rat 对象转换为字符串形式,格式为 分子/分母。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import (
	"fmt"
	"math/big"
)

func main() {
	rat := big.NewRat(5, 7)
	s := rat.String()
	fmt.Println(s)
}

big.Rat 类型在需要精确表示和计算有理数的场景中非常有用,如金融计算、科学计算等领域,避免了浮点数运算可能带来的精度问题。


相关内容

0%