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 是一个专为金融计算设计的高性能定点十进制计算库,具有零内存分配、高精度和线程安全等特点,非常适合需要高性能计算的金融场景。

主要特性

  1. 零内存分配:避免GC压力,提高性能
  2. 定点十进制:精确表示金融数值
  3. 高性能:比标准库big.Decimal快10-100倍
  4. 线程安全:所有操作都是并发安全的
  5. 丰富的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)
}

性能优化技巧

  1. 重用Decimal对象:使用Reset方法重用对象减少分配

    var temp udecimal.Decimal
    for i := 0; i < 1000; i++ {
        temp.Reset().Add(d1).Mul(d2)
        // 使用temp...
    }
    
  2. 使用预分配切片:批量处理时预分配结果切片

    results := make([]udecimal.Decimal, len(inputs))
    for i, input := range inputs {
        results[i] = input.Mul(factor)
    }
    
  3. 合理设置精度:避免不必要的过高精度计算

与标准库对比

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))
}

注意事项

  1. 最大支持18位小数精度
  2. 所有操作都是线程安全的
  3. 零分配特性在循环和热点路径中表现最佳
  4. 对于极高精度需求(>18位),可能需要考虑其他库

udecimal 是金融计算和高性能数值处理的理想选择,特别适合交易系统、风险计算和批量处理等场景。

回到顶部