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库的核心特性

  1. 高精度:支持任意精度的十进制运算
  2. 不可变:所有操作都返回新对象,确保线程安全
  3. 安全:避免浮点数精度问题
  4. 功能丰富:提供各种数学运算和舍入模式

安装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)

性能考虑

  1. 对于性能敏感的场景,可以复用Decimal对象减少内存分配
  2. 避免频繁的字符串转换
  3. 对于简单整数运算,使用原生类型更快

总结

decimal库为Go提供了安全、精确的十进制运算能力,特别适合:

  • 金融计算
  • 货币处理
  • 税务计算
  • 任何需要精确小数运算的场景

通过合理使用decimal库,可以避免浮点数精度问题,确保计算结果的准确性。

希望这个指南能帮助你理解和使用decimal库!如需更高级功能,可以参考官方文档。

回到顶部