golang高性能零分配金融级定点十进制计算插件库udecimal的使用
Golang高性能零分配金融级定点十进制计算插件库udecimal的使用
概述
udecimal是一个高性能、高精度、零内存分配的定点十进制计算库,专为金融应用设计。它具有以下特点:
- 高精度:支持最多19位小数,算术运算无精度损失
- 零内存分配:99%的情况下无需内存分配
- 优化速度:比shopspring/decimal和ericlagergren/decimal快5~20倍
- 无panic:所有错误都作为值返回
- 并发安全:所有算术操作返回新值,原始值保持不变
- 正确性:通过模糊测试并与shopspring/decimal交叉验证
- 多种舍入方法:包括HALF AWAY FROM ZERO、HALF TOWARD ZERO、AWAY FROM ZERO和Banker舍入
安装
支持Go版本>=1.23
go get github.com/quagmt/udecimal
使用示例
基本算术运算
package main
import (
"fmt"
"github.com/quagmt/udecimal"
)
func main() {
// 创建新的十进制数
a, _ := udecimal.NewFromInt64(123456, 3) // a = 123.456
b, _ := udecimal.NewFromInt64(-123456, 4) // b = -12.3456
c, _ := udecimal.NewFromFloat64(1.2345) // c = 1.2345
d, _ := udecimal.Parse("4123547.1234567890123456789") // d = 4123547.1234567890123456789
// 基本算术运算
fmt.Println(a.Add(b)) // 123.456 - 12.3456 = 111.1104
fmt.Println(a.Sub(b)) // 123.456 + 12.3456 = 135.8016
fmt.Println(a.Mul(b)) // 123.456 * -12.3456 = -1524.1383936
fmt.Println(a.Div(b)) // 123.456 / -12.3456 = -10
fmt.Println(a.Div(d)) // 123.456 / 4123547.1234567890123456789 = 0.0000299392722585176
// 舍入
fmt.Println(c.RoundBank(3)) // banker's rounding: 1.2345 -> 1.234
fmt.Println(c.RoundAwayFromZero(2)) // round away from zero: 1.2345 -> 1.24
fmt.Println(c.RoundHAZ(3)) // half away from zero: 1.2345 -> 1.235
fmt.Println(c.RoundHTZ(3)) // half towards zero: 1.2345 -> 1.234
fmt.Println(c.Trunc(2)) // truncate: 1.2345 -> 1.23
fmt.Println(c.Floor()) // floor: 1.2345 -> 1
fmt.Println(c.Ceil()) // ceil: 1.2345 -> 2
// 显示
fmt.Println(a.String()) // 123.456
fmt.Println(a.StringFixed(10)) // 123.4560000000
fmt.Println(a.InexactFloat64()) // 123.456
}
舍入方法示例
package main
import (
"fmt"
"github.com/quagmt/udecimal"
)
func main() {
// 创建新的十进制数
a, _ := udecimal.NewFromFloat64(1.345) // a = 1.345
// 舍入
fmt.Println(a.RoundBank(2)) // banker's rounding: 1.345 -> 1.34
fmt.Println(a.RoundAwayFromZero(2)) // round away from zero: 1.345 -> 1.35
fmt.Println(a.RoundHAZ(2)) // half away from zero: 1.345 -> 1.35
fmt.Println(a.RoundHTZ(2)) // half towards zero: 1.345 -> 1.34
}
工作原理
udecimal使用固定点十进制表示法,由三个部分组成:符号、系数和精度。数字表示为:
// decimal value = (neg == true ? -1 : 1) * coef * 10^(-prec)
type Decimal struct {
coef bint
neg bool
prec uint8 // 0 <= prec <= 19
}
系数使用自定义的bint
类型存储:
type bint struct {
// 对于超过u128的系数
bigInt *big.Int
// 对于小于2^128的系数
u128 u128
}
在大多数情况下,系数可以存储在128位无符号整数中,此时运算快速且无需内存分配。只有在极少数情况下(当结果超过128位时)才会使用big.Int
,此时会有内存分配和性能下降。
为什么选择udecimal
与其他十进制库相比,udecimal具有以下优势:
- 比shopspring/decimal更快且内存分配更少
- 比cockroachdb/apd更直观的API
- 比govalues/decimal更大的数值范围
注意事项
该库不执行隐式舍入。如果操作结果超过最大精度,额外的数字将被截断。所有舍入方法必须显式调用。
更多关于golang高性能零分配金融级定点十进制计算插件库udecimal的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang高性能零分配金融级定点十进制计算插件库udecimal的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
udecimal - 高性能零分配金融级定点十进制计算库
udecimal 是一个专为金融计算设计的高性能定点十进制计算库,具有零内存分配、高精度和线程安全等特点,非常适合需要高性能计算的金融场景。
主要特性
- 零内存分配:避免GC压力,提高性能
- 定点十进制:精确表示金融数值
- 高性能:比标准库
big.Decimal
快10-100倍 - 线程安全:所有操作都是并发安全的
- 丰富的API:支持各种金融计算需求
安装
go get github.com/universe-xyz/udecimal
基本使用示例
package main
import (
"fmt"
"github.com/universe-xyz/udecimal"
)
func main() {
// 创建Decimal值
d1 := udecimal.MustNewFromString("123.456")
d2 := udecimal.MustNewFromString("789.012")
// 加法
sum := d1.Add(d2)
fmt.Println("Sum:", sum) // 输出: Sum: 912.468
// 减法
diff := d2.Sub(d1)
fmt.Println("Difference:", diff) // 输出: Difference: 665.556
// 乘法
product := d1.Mul(d2)
fmt.Println("Product:", product) // 输出: Product: 97408.265472
// 除法
quotient := d2.Div(d1, 6) // 6位小数精度
fmt.Println("Quotient:", quotient) // 输出: Quotient: 6.388889
// 比较
fmt.Println("d1 > d2:", d1.GreaterThan(d2)) // 输出: d1 > d2: false
fmt.Println("d1 == d2:", d1.Equal(d2)) // 输出: d1 == d2: false
}
高级使用示例
金融计算场景
func calculateInterest(principal udecimal.Decimal, rate udecimal.Decimal, days int) udecimal.Decimal {
// 日利率 = 年利率 / 365
dailyRate := rate.Div(udecimal.NewFromInt(365), 10)
// 利息 = 本金 * 日利率 * 天数
interest := principal.Mul(dailyRate).Mul(udecimal.NewFromInt(int64(days)))
// 四舍五入到2位小数
return interest.Round(2)
}
func main() {
principal := udecimal.MustNewFromString("10000.00") // 本金1万元
rate := udecimal.MustNewFromString("0.05") // 年利率5%
days := 30 // 30天
interest := calculateInterest(principal, rate, days)
fmt.Println("30天利息:", interest) // 输出: 30天利息: 41.10
}
批量处理高性能场景
func batchProcess(prices []udecimal.Decimal, quantity []udecimal.Decimal) udecimal.Decimal {
total := udecimal.Zero()
for i := 0; i < len(prices); i++ {
// 计算每个项目的金额并累加
amount := prices[i].Mul(quantity[i])
total = total.Add(amount)
}
return total.Round(2) // 四舍五入到2位小数
}
func main() {
// 模拟批量数据
prices := make([]udecimal.Decimal, 1000)
quantities := make([]udecimal.Decimal, 1000)
for i := 0; i < 1000; i++ {
prices[i] = udecimal.NewFromInt(int64(i%100 + 1)).Add(udecimal.NewFromFloat(0.99))
quantities[i] = udecimal.NewFromInt(int64(i%10 + 1))
}
total := batchProcess(prices, quantities)
fmt.Println("总金额:", total)
}
性能优化技巧
-
重用Decimal对象:使用
Reset
方法重用对象减少分配var temp udecimal.Decimal for i := 0; i < 1000; i++ { temp.Reset().Add(d1).Mul(d2) // 使用temp... }
-
使用预分配切片:批量处理时预分配结果切片
results := make([]udecimal.Decimal, len(inputs)) for i, input := range inputs { results[i] = input.Mul(factor) }
-
合理设置精度:避免不必要的过高精度计算
与标准库对比
func benchmarkComparison() {
bigDecimal1, _ := decimal.NewFromString("123.456")
bigDecimal2, _ := decimal.NewFromString("789.012")
uDecimal1 := udecimal.MustNewFromString("123.456")
uDecimal2 := udecimal.MustNewFromString("789.012")
// 标准库decimal性能
start := time.Now()
for i := 0; i < 100000; i++ {
bigDecimal1.Add(bigDecimal2)
}
fmt.Println("decimal.Add:", time.Since(start))
// udecimal性能
start = time.Now()
for i := 0; i < 100000; i++ {
uDecimal1.Add(uDecimal2)
}
fmt.Println("udecimal.Add:", time.Since(start))
}
注意事项
- 最大支持18位小数精度
- 所有操作都是线程安全的
- 零分配特性在循环和热点路径中表现最佳
- 对于极高精度需求(>18位),可能需要考虑其他库
udecimal 是金融计算和高性能数值处理的理想选择,特别适合交易系统、风险计算和批量处理等场景。