# 04.7 计算Pi的精确值

在本小节中你将学习到如何使用Go标准库`math/big`以及其提供的特殊类型，并计算高精度的**Pi**值。

> *本节的代码是我所见过的最丑陋的Go代码，甚至用Java写看起来都会好一些。*

`calculatePi.go`使用Bellard规则计算Pi值，代码将分4部分展示。

第一部分：

> ```go
> package main
>
> import (
>    "fmt"
>    "math"
>    "math/big"
>    "os"
>    "strconv"
> )
>
> var precision uint = 0
> ```

`precision`变量代表你想得到的Pi值精度，其声明为全局变量保证在整个程序中都可以被访问到。

第二部分：

> ```go
> func Pi(accuracy uint) *big.Float {
>    k := 0
>    pi := new(big.Float).SetPrec(precision).SetFloat64(0)
>    k1k2k3 := new(big.Float).SetPrec(precision).SetFloat64(0)
>    k4k5k6 := new(big.Float).SetPrec(precision).SetFloat64(0)
>    temp := new(big.Float).SetPrec(precision).SetFloat64(0)
>    minusOne := new(big.Float).SetPrec(precision).SetFloat64(-1)
>    total := new(big.Float).SetPrec(precision).SetFloat64(0)
>
>    two2Six := math.Pow(2, 6)
>    two2SixBig := new(big.Float).SetPrec(precision).SetFloat64(two2Six)
> ```

`new(big.Float)`创建一个`big.Float`类型的变量，并调用`SetPrec()`函数将精度设置为参数`precision`。

第三部分是贝拉算法计算精确Pi值的函数`Pi()`实现（关于贝拉算法可查看文末简介）：

> ```go
> for {
>       if k > int(accuracy) {
>          break
>       }
>       t1 := float64(float64(1) / float64(10*k+9))
>       k1 := new(big.Float).SetPrec(precision).SetFloat64(t1)
>       t2 := float64(float64(64) / float64(10*k+3))
>       k2 := new(big.Float).SetPrec(precision).SetFloat64(t2)
>       t3 := float64(float64(32) / float64(4*k+1))
>       k3 := new(big.Float).SetPrec(precision).SetFloat64(t3)
>       k1k2k3.Sub(k1, k2)
>       k1k2k3.Sub(k1k2k3, k3)
>
>       t4 := float64(float64(4) / float64(10*k+5))
>       k4 := new(big.Float).SetPrec(precision).SetFloat64(t4)
>       t5 := float64(float64(4) / float64(10*k+7))
>       k5 := new(big.Float).SetPrec(precision).SetFloat64(t5)
>       t6 := float64(float64(1) / float64(4*k+3))
>       k6 := new(big.Float).SetPrec(precision).SetFloat64(t6)
>       k4k5k6.Add(k4, k5)
>       k4k5k6.Add(k4k5k6, k6)
>       k4k5k6 = k4k5k6.Mul(k4k5k6, minusOne)
>       temp.Add(k1k2k3, k4k5k6)
>
>       k7temp := new(big.Int).Exp(big.NewInt(-1), big.NewInt(int64(k)), nil)
>       k8temp := new(big.Int).Exp(big.NewInt(1024), big.NewInt(int64(k)), nil)
>
>       k7 := new(big.Float).SetPrec(precision).SetFloat64(0)
>       k7.SetInt(k7temp)
>       k8 := new(big.Float).SetPrec(precision).SetFloat64(0)
>       k8.SetInt(k8temp)
>
>       t9 := float64(256) / float64(10*k+1)
>       k9 := new(big.Float).SetPrec(precision).SetFloat64(t9)
>       k9.Add(k9, temp)
>       total.Mul(k9, k7)
>       total.Quo(total, k8)
>       pi.Add(pi, total)
>
>       k = k + 1
>    }
>    pi.Quo(pi, two2SixBig)
>    return pi
> }
> ```

这部分代码是贝拉算法的Go实现，你必须借助于`math/big`包中特定的函数进行计算，因为这些函数能够实现你想要达到的数字精度，可以说如果不使用`big.Float`、`big.Int`变量以及`math/big`中的函数，高精度的Pi值根本无法计算。

最后一部分代码：

> ```go
> func main() {
>    arguments := os.Args
>    if len(arguments) == 1 {
>       fmt.Println("Please provide one numeric argument!")
>       os.Exit(1)
>    }
>
>    temp, _ := strconv.ParseUint(arguments[1], 10, 32)
>    precision = uint(temp) * 3
>
>    PI := Pi(precision)
>    fmt.Println(PI)
> }
> ```

执行`calculatePi.go`得到如下输出：

> $ go run calculatePi.go
>
> Please provide one numeric argument!
>
> exit status 1
>
> $ go run calculatePi.go 20
>
> 3.141592653589793258
>
> $ go run calculatePi.go 200
>
> 3.141592653589793256960399361738762404019183156248573243493179283571046450248913467118511784317615354282017929416292809050813937875283435610586313363548602436768047706489838924381929

本节的代码需要使用很多不同的数据类型，务必保证数据类型的正确使用！

*课外阅读*

> 文中提到的贝拉算法简介：
>
> *Fabrice Bellard在圆周率算法方面也有着惊人的成就，1997年FabriceBellard提出最快圆周率算法公式。在计算圆周率的过程中，Fabrice Bellard使用改良后的查德诺夫斯基方程算法来进行圆周率的计算，并使用贝利-波温-劳夫算法来验证计算的结果。为了纪念他对圆周率算法所作出的杰出贡献，Fabrice Bellard所使用的改良型算法被命名为Fabrice Bellard算法，这种算法是目前所有圆周率算法中最快的一种，这个计算N位PI的公式比传统的BBQ算法要快47%。 2009年的最后一天，Fabr ice Bellard宣布另一重大突破：他用桌面电脑打破了由超级计算机保持的圆周率运算记录。这是一个壮举， 他将PI计算到了小数点后2.7万亿位！更令人惊讶的是， 他使用的不过是价格不到2000欧元的个人PC，仅用了116天，就计算出了PI的小数点后第2.7万亿位，超过了排名世界第47位的T2K Open超级计算机于2009年8月17日创造的世界纪录。新纪录比原纪录多出1200亿位，然而，他使用的这台桌面电脑的配置仅为：2.93GHz Core i7 CPU，6GB内存，7.5TB硬盘! 不过这次为了加快计算完成的速度保住排名第一的位置，Fabrice Bel lard使用了9台联网的电脑来对数据进行验证， 若使用一台电脑来验证计算结果的话， 则需要额外增加13天的计算时间。 Fabrice Bellard在圆周率方面的辉煌成就， 使他创造多次圆周率单一位计算的世界纪录（计算10的整次幂位） ， 也曾因此而登上《*[*科学美国人*](https://baike.baidu.com/item/科学美国人/810009)*》法文版。*


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wskdsgcf.gitbook.io/mastering-go-zh-cn/04.0/04.7.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
