shopspring/decimal

GitHub: shopspring/decimal

Go 语言的任意精度定点十进制数库,旨在解决浮点数精度丢失问题,特别适合财务与货币计算。

Stars: 7450 | Forks: 673

# 十进制 [![ci](https://static.pigsec.cn/wp-content/uploads/repos/cas/99/993938d8ce5e902ccfb9d6747725c320d855dea3235ed9a304cedf0d94c9321f.svg)](https://github.com/shopspring/decimal/actions/workflows/ci.yml) [![GoDoc](https://godoc.org/github.com/shopspring/decimal?status.svg)](https://godoc.org/github.com/shopspring/decimal) [![Go Report Card](https://goreportcard.com/badge/github.com/shopspring/decimal)](https://goreportcard.com/report/github.com/shopspring/decimal) go 语言中的任意精度定点十进制数。 _注意:_ decimal 库“只能”表示小数点后最多 2^31 位的数字。 ## 特性 * 零值为 0,无需初始化即可安全使用 * 加法、减法、乘法无精度损失 * 支持指定精度的除法 * 支持 database/sql 序列化/反序列化 * 支持 JSON 和 XML 序列化/反序列化 ## 安装 运行 `go get github.com/shopspring/decimal` ## 环境要求 decimal 库要求 Go 版本 `>=1.10` ## 文档 http://godoc.org/github.com/shopspring/decimal ## 用法 ``` package main import ( "fmt" "github.com/shopspring/decimal" ) func main() { price, err := decimal.NewFromString("136.02") if err != nil { panic(err) } quantity := decimal.NewFromInt(3) fee, _ := decimal.NewFromString(".035") taxRate, _ := decimal.NewFromString(".08875") subtotal := price.Mul(quantity) preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1))) total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1))) fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06 fmt.Println("Pre-tax:", preTax) // Pre-tax: 422.3421 fmt.Println("Taxes:", total.Sub(preTax)) // Taxes: 37.482861375 fmt.Println("Total:", total) // Total: 459.824961375 fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875 } ``` ## 备选库 在处理十进制数时,您可能会遇到本库并不完美适用的问题。 幸运的是,感谢我们出色的社区,还有许多其他的库供您选择。 探索其他备选方案,找到最适合您需求的吧 :) * [cockroachdb/apd](https://github.com/cockroachdb/apd) - 任意精度,可变且具有类似于 `big.Int` 的丰富 API,性能比本库更高 * [alpacahq/alpacadecimal](https://github.com/alpacahq/alpacadecimal) - 高性能,低精度(12 位),与本库完全兼容的 API * [govalues/decimal](https://github.com/govalues/decimal) - 高性能,零内存分配,低精度(19 位) * [greatcloak/decimal](https://github.com/greatcloak/decimal) - 专注于账单和电子商务 Web 应用相关用例的分支,包含开箱即用的 BSON 序列化支持 ## 常见问题 #### 为什么不直接使用 float64? 因为 float64(实际上,任何二进制浮点数类型)都无法精确表示像 `0.1` 这样的数字。 考虑一下这段代码:http://play.golang.org/p/TQBd4yJe6B 您可能期望它输出 `10`,但实际上它输出的是 `9.999999999999831`。随着时间的推移,这些微小的误差会不断累加! #### 为什么不直接使用 big.Rat? big.Rat 适合表示有理数,但 Decimal 更适合表示货币。为什么?这是一个(虚构的)例子: 假设您使用 big.Rat,并且有两个数字 x 和 y,它们都表示 1/3,并且您有 `z = 1 - x - y = 1/3`。如果您分别将它们打印出来,字符串输出必须在某处停止(为了简单起见,假设它在小数点后 3 位停止),那么您将得到 0.333、0.333 和 0.333。但是另外的 0.001 去哪了呢? 这是上述例子的代码形式:http://play.golang.org/p/lCZZs0w9KE 使用 Decimal 时,打印出来的字符串精确地表示了该数字。因此,如果您有 `x = y = 1/3`(精度为 3),它们实际上将等于 0.333,并且当您执行 `z = 1 - x - y` 时,`z` 将等于 .334。没有任何资金会去向不明! 您仍然需要小心。如果您想将一个数字 `N` 分成 3 份,您不能直接将 `N/3` 发送给三个不同的人。您必须选择一个人发送 `N - (2/3*N)`。那个人将收到作为余数的一分钱的一部分。 但是,使用 Decimal 比使用 big.Rat 更容易做到谨慎操作。 #### 为什么 API 不类似于 big.Int 的 API? big.Int 的 API 旨在减少内存分配次数以实现最大性能。这对于其用例来说是有意义的,但代价是 API 显得笨拙且容易误用。 例如,要将两个 big.Int 相加,您需要这样做:`z := new(big.Int).Add(x, y)`。不熟悉此 API 的开发者可能会尝试执行 `z := a.Add(a, b)`。这会修改 `a` 并将 `z` 设置为 `a` 的别名,这可能出乎他们的意料。它还会修改 `a` 的任何其他别名。 这是一个使用 big.Int 的 API 可能引入的微妙 bug 的例子: https://play.golang.org/p/x2R_78pa8r 相比之下,使用 decimal 很难犯这样的错误。Decimal 的行为与其他 go 数字类型一样:即使 `a = b` 不会将 `b` 深拷贝到 `a` 中,修改一个 Decimal 也是不可能的,因为所有的 Decimal 方法都返回新的 Decimal,而不会修改原始值。其缺点是这会导致额外的内存分配,因此 Decimal 的性能较低。我的假设是,如果您使用 Decimal,您可能更关心正确性而不是性能。 ## 许可证 The MIT License (MIT) 这是 [fpd.Decimal](https://github.com/oguzbilgic/fpd) 的一个深度修改的分支,它同样是在 MIT 许可证下发布的。
标签:EVTX分析, Go, Homebrew安装, Ruby工具, 定点小数, 数学库, 日志审计, 高精度计算