golang实现高精度不可变十进制数安全运算插件库decimal的使用
golang实现高精度不可变十进制数安全运算插件库decimal的使用
decimal是一个实现了精确舍入的十进制浮点数运算的Go语言包,专为金融交易系统设计。
主要特性
- BSON、JSON、XML、SQL支持 - 实现了与mongo-driver/bson、encoding/json、encoding/xml和database/sql包直接兼容的必要接口
- 无堆分配 - 优化以避免堆分配,防止算术操作期间的垃圾收集器影响
- 正确舍入 - 所有方法的结果都是使用"银行家舍入法"将真实数学值舍入到19位精度
- 无panic - 所有方法都不会panic,在溢出或除以零等情况下返回错误而非崩溃
- 不可变性 - 一旦设置,decimal保持恒定,确保跨goroutine的安全并发访问
- 简单字符串表示 - 十进制数以简单格式表示,避免科学或工程记数法的复杂性
- 严格测试 - 所有方法都通过广泛的模糊测试与cockroachdb/apd和shopspring/decimal包进行交叉验证
安装
在你的Go工作区中添加decimal包:
go get github.com/govalues/decimal
基本使用
使用构造函数创建decimal值。创建后,可以执行各种操作:
package main
import (
"fmt"
"github.com/govalues/decimal"
)
func main() {
// 构造函数
d, _ := decimal.New(8, 0) // d = 8
e, _ := decimal.Parse("12.5") // e = 12.5
f, _ := decimal.NewFromFloat64(2.567) // f = 2.567
g, _ := decimal.NewFromInt64(7, 896, 3) // g = 7.896
// 算术运算
fmt.Println(d.Add(e)) // 8 + 12.5
fmt.Println(d.Sub(e)) // 8 - 12.5
fmt.Println(d.SubAbs(e)) // abs(8 - 12.5)
fmt.Println(d.Mul(e)) // 8 * 12.5
fmt.Println(d.AddMul(e, f)) // 8 + 12.5 * 2.567
fmt.Println(d.SubMul(e, f)) // 8 - 12.5 * 2.567
fmt.Println(d.PowInt(2)) // 8²
fmt.Println(d.Quo(e)) // 8 / 12.5
fmt.Println(d.AddQuo(e, f)) // 8 + 12.5 / 2.567
fmt.Println(d.SubQuo(e, f)) // 8 - 12.5 / 2.567
fmt.Println(d.QuoRem(e)) // 8 div 12.5, 8 mod 12.5
fmt.Println(d.Inv()) // 1 / 8
fmt.Println(decimal.Sum(d, e, f)) // 8 + 12.5 + 2.567
fmt.Println(decimal.Mean(d, e, f)) // (8 + 12.5 + 2.567) / 3
fmt.Println(decimal.Prod(d, e, f)) // 8 * 12.5 * 2.567
// 超越函数
fmt.Println(e.Sqrt()) // √12.5
fmt.Println(e.Exp()) // exp(12.5)
fmt.Println(e.Expm1()) // exp(12.5) - 1
fmt.Println(e.Log()) // ln(12.5)
fmt.Println(e.Log1p()) // ln(12.5 + 1)
fmt.Println(e.Log2()) // log₂(12.5)
fmt.Println(e.Log10()) // log₁₀(12.5)
fmt.Println(e.Pow(d)) // 12.5⁸
// 四舍五入到2位小数
fmt.Println(g.Round(2)) // 7.90
fmt.Println(g.Ceil(2)) // 7.90
fmt.Println(g.Floor(2)) // 7.89
fmt.Println(g.Trunc(2)) // 7.89
// 转换
fmt.Println(f.Int64(9)) // 2 567000000
fmt.Println(f.Float64()) // 2.567
fmt.Println(f.String()) // 2.567
// 格式化
fmt.Printf("%.2f", f) // 2.57
fmt.Printf("%.2k", f) // 256.70%
}
与其他包的比较
特性 | govalues | cockroachdb/apd v3.2.1 | shopspring/decimal v1.4.0 |
---|---|---|---|
正确舍入 | 是 | 否 | 否 |
速度 | 高 | 中 | 低 |
堆分配 | 无 | 中 | 高 |
精度 | 19位 | 任意 | 任意 |
无panic | 是 | 是 | 否 |
可变性 | 不可变 | 可变 | 不可变 |
数学上下文 | 隐式 | 显式 | 隐式 |
性能基准
以下是部分基准测试结果(单位:纳秒):
测试用例 | 表达式 | govalues | cockroachdb/apd | shopspring/decimal | govalues vs cockroachdb | govalues vs shopspring |
---|---|---|---|---|---|---|
Add | 5 + 6 | 16.06n | 74.88n | 140.90n | +366.22% | +777.33% |
Mul | 2 * 3 | 16.93n | 62.20n | 146.00n | +267.40% | +762.37% |
Quo | 2 / 4 (exact) | 59.52n | 176.95n | 657.40n | +197.30% | +1004.50% |
Sqrt | √2 | 3.40µ | 4.96µ | 2101.86µ | +46.00% | +61755.71% |
注意:表中显示的基准测试结果仅供参考,实际结果可能因具体用例而异。
更多关于golang实现高精度不可变十进制数安全运算插件库decimal的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang实现高精度不可变十进制数安全运算插件库decimal的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang高精度十进制数运算:decimal库使用指南
decimal是Go语言中一个优秀的高精度十进制数运算库,特别适合金融计算等需要精确小数运算的场景。下面我将详细介绍如何使用decimal库。
decimal库的核心特性
- 高精度:支持任意精度的十进制运算
- 不可变:所有操作都返回新对象,确保线程安全
- 安全:避免浮点数精度问题
- 功能丰富:提供各种数学运算和舍入模式
安装decimal库
go get github.com/shopspring/decimal
基本使用示例
1. 创建Decimal对象
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
// 从字符串创建
d1 := decimal.RequireFromString("123.456")
// 从整数创建
d2 := decimal.NewFromInt(789)
// 从float创建(不推荐,可能有精度问题)
d3 := decimal.NewFromFloat(3.1415926)
fmt.Println(d1, d2, d3)
}
2. 基本算术运算
a := decimal.RequireFromString("10.5")
b := decimal.RequireFromString("3.2")
// 加法
sum := a.Add(b)
// 减法
diff := a.Sub(b)
// 乘法
product := a.Mul(b)
// 除法(带精度控制)
quotient := a.Div(b).Round(4) // 保留4位小数
// 取余
remainder := a.Mod(b)
fmt.Println("Sum:", sum, "Diff:", diff, "Product:", product,
"Quotient:", quotient, "Remainder:", remainder)
3. 比较运算
x := decimal.RequireFromString("100.00")
y := decimal.RequireFromString("100.000")
// 相等比较(自动忽略尾随零)
if x.Equal(y) {
fmt.Println("x equals y")
}
// 大小比较
if x.GreaterThan(decimal.NewFromInt(50)) {
fmt.Println("x > 50")
}
4. 舍入和格式化
price := decimal.RequireFromString("123.456789")
// 四舍五入到2位小数
rounded := price.Round(2)
// 向下取整
floor := price.RoundFloor(2)
// 向上取整
ceil := price.RoundCeil(2)
// 格式化为字符串
str := rounded.String()
fmt.Println("Rounded:", rounded, "Floor:", floor, "Ceil:", ceil, "String:", str)
5. 金融计算示例
// 计算利息
principal := decimal.RequireFromString("10000.00")
rate := decimal.RequireFromString("0.05") // 5%
years := decimal.NewFromInt(3)
interest := principal.Mul(rate).Mul(years).Round(2)
total := principal.Add(interest)
fmt.Println("Interest:", interest, "Total:", total)
// 分期付款计算
loanAmount := decimal.RequireFromString("200000.00")
monthlyRate := decimal.RequireFromString("0.004167") // 月利率0.4167%
months := decimal.NewFromInt(360) // 30年
// PMT公式: P * r * (1+r)^n / ((1+r)^n - 1)
one := decimal.NewFromInt(1)
temp := one.Add(monthlyRate).Pow(months)
monthlyPayment := loanAmount.Mul(monthlyRate).Mul(temp).Div(temp.Sub(one)).Round(2)
fmt.Println("Monthly Payment:", monthlyPayment)
高级特性
1. 自定义上下文(精度和舍入模式)
// 创建自定义上下文
highPrecision := decimal.Context{
Precision: 20,
RoundingMode: decimal.RoundHalfUp,
}
a := decimal.RequireFromString("1")
b := decimal.RequireFromString("3")
// 使用自定义上下文进行除法
result := highPrecision.Quo(a, b)
fmt.Println("1/3 with high precision:", result)
2. 序列化和反序列化
// JSON序列化
type Invoice struct {
Amount decimal.Decimal `json:"amount"`
}
invoice := Invoice{Amount: decimal.RequireFromString("123.45")}
jsonData, _ := json.Marshal(invoice)
fmt.Println(string(jsonData)) // {"amount":"123.45"}
// JSON反序列化
var newInvoice Invoice
json.Unmarshal(jsonData, &newInvoice)
fmt.Println(newInvoice.Amount)
性能考虑
- 对于性能敏感的场景,可以复用Decimal对象减少内存分配
- 避免频繁的字符串转换
- 对于简单整数运算,使用原生类型更快
总结
decimal库为Go提供了安全、精确的十进制运算能力,特别适合:
- 金融计算
- 货币处理
- 税务计算
- 任何需要精确小数运算的场景
通过合理使用decimal库,可以避免浮点数精度问题,确保计算结果的准确性。
希望这个指南能帮助你理解和使用decimal库!如需更高级功能,可以参考官方文档。