golang高性能固定小数位精确序列化与算术运算插件库fpdecimal的使用

Golang高性能固定小数位精确序列化与算术运算插件库fpdecimal的使用

fpdecimal是一个高性能的固定小数位精确计算库,特别适合金融货币计算场景。它基于int64实现,避免了浮点数计算带来的精度问题。

主要特性

  • 内部使用int64存储
  • 不使用float类型进行解析或打印
  • 性能极高:比float快3倍,比shopspring/decimal快20倍,比fmt快30倍
  • 零开销设计
  • 防止容易出错的定点运算
  • 包含模糊测试和基准测试
  • 支持JSON序列化
  • 仅200行代码

安装

import fp "github.com/nikolaydubina/fpdecimal"

使用示例

// 定义常量价格
var BuySP500Price = fp.FromInt(9000)

// 解析JSON数据
input := []byte(`{"sp500": 9000.023}`)

// 定义结构体
type Stocks struct {
    SP500 fp.Decimal `json:"sp500"`
}
var v Stocks
if err := json.Unmarshal(input, &v); err != nil {
    log.Fatal(err)
}

// 根据条件进行计算
var amountToBuy fp.Decimal
if v.SP500.GreaterThan(BuySP500Price) {
    amountToBuy = amountToBuy.Add(v.SP500.Mul(fp.FromInt(2)))
}

fmt.Println(amountToBuy)
// 输出: 18000.046

性能基准测试

解析性能

BenchmarkParse/fromString/small-10     534307098   11.36 ns/op   0 B/op   0 allocs/op
BenchmarkParse/fromString/large-10     254741558   23.42 ns/op   0 B/op   0 allocs/op
BenchmarkParse/UnmarshalJSON/small-10 816873427    7.32 ns/op   0 B/op   0 allocs/op
BenchmarkParse/UnmarshalJSON/large-10 272173255   22.16 ns/op   0 B/op   0 allocs/op

打印性能

BenchmarkPrint/small-10      191982066   31.24 ns/op   8 B/op   1 allocs/op
BenchmarkPrint/large-10      150874335   39.89 ns/op  24 B/op   1 allocs/op

算术运算性能

BenchmarkArithmetic/add-10     1000000000   0.316 ns/op   0 B/op   0 allocs/op
BenchmarkArithmetic/div-10     1000000000   0.950 ns/op   0 B/op   0 allocs/op
BenchmarkArithmetic/divmod-10  1000000000   1.890 ns/op   0 B/op   0 allocs/op

为什么适合货币计算

  • 全球只有约200种货币
  • 所有货币最多有3位小数
  • 即使没有小数位的货币通常也比美元大1000倍
  • int64最大可表示9,223,372,036,854,775.807(约9千万亿),足以满足大多数货币计算需求

完整示例

package main

import (
	"encoding/json"
	"fmt"
	"log"
	
	fp "github.com/nikolaydubina/fpdecimal"
)

func main() {
	// 创建Decimal值
	price := fp.FromInt(1000) // 1000.000
	discount := fp.MustFromString("150.500")
	
	// 计算最终价格
	finalPrice := price.Sub(discount)
	
	// 序列化为JSON
	type Order struct {
		OriginalPrice fp.Decimal `json:"original_price"`
		FinalPrice    fp.Decimal `json:"final_price"`
	}
	
	order := Order{
		OriginalPrice: price,
		FinalPrice:    finalPrice,
	}
	
	jsonData, err := json.Marshal(order)
	if err != nil {
		log.Fatal(err)
	}
	
	fmt.Println(string(jsonData))
	// 输出: {"original_price":1000.000,"final_price":849.500}
	
	// 反序列化
	var newOrder Order
	if err := json.Unmarshal(jsonData, &newOrder); err != nil {
		log.Fatal(err)
	}
	
	fmt.Printf("反序列化结果: %+v\n", newOrder)
	// 输出: 反序列化结果: {OriginalPrice:1000.000 FinalPrice:849.500}
	
	// 更多运算
	tax := newOrder.FinalPrice.Mul(fp.MustFromString("0.1")) // 10%税
	total := newOrder.FinalPrice.Add(tax)
	
	fmt.Println("含税总价:", total)
	// 输出: 含税总价: 934.450
}

注意事项

  • 不要直接使用算术运算符,而是使用库提供的方法
  • 乘法要注意小数位数会增加
  • 加法和减法要注意常量的缩放因子

fpdecimal提供了高性能且精确的定点小数运算,特别适合金融和货币计算场景。


更多关于golang高性能固定小数位精确序列化与算术运算插件库fpdecimal的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高性能固定小数位精确序列化与算术运算插件库fpdecimal的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


fpdecimal: Golang高性能固定小数位精确序列化与算术运算库

fpdecimal 是一个专为Golang设计的高性能固定小数位精确序列化与算术运算库,特别适合金融、会计等需要高精度计算的场景。

主要特性

  1. 固定小数位数(默认6位)
  2. 高性能算术运算
  3. 精确序列化/反序列化
  4. 零内存分配设计
  5. 线程安全

安装

go get github.com/jiangz222/fpdecimal

基本用法

初始化Decimal

import "github.com/jiangz222/fpdecimal"

// 从整数创建
d1 := fpdecimal.FromInt(100) // 100.000000

// 从字符串创建
d2, err := fpdecimal.FromString("123.456789")

// 从float64创建
d3 := fpdecimal.FromFloat(78.9) // 78.900000

算术运算

a := fpdecimal.FromInt(100)
b := fpdecimal.FromInt(200)

// 加法
sum := a.Add(b) // 300.000000

// 减法
diff := b.Sub(a) // 100.000000

// 乘法
product := a.Mul(b) // 20000.000000

// 除法
quotient, err := b.Div(a) // 2.000000

// 取余
remainder := b.Mod(a) // 0.000000

比较运算

a := fpdecimal.FromInt(100)
b := fpdecimal.FromInt(200)

a.Equal(b) // false
a.LessThan(b) // true
a.GreaterThan(b) // false

序列化

d := fpdecimal.FromInt(123456)

// 转为字符串
str := d.String() // "123456.000000"

// 转为JSON
jsonBytes, _ := json.Marshal(d) // "123456.000000"

// 保留2位小数
str2 := d.StringFixed(2) // "123456.00"

反序列化

// 从JSON解析
var d fpdecimal.Decimal
json.Unmarshal([]byte(`"123.456"`), &d)

// 从字符串解析
d2, err := fpdecimal.FromString("789.012")

高级用法

自定义小数位数

// 创建自定义6位小数的Decimal类型
type MyDecimal fpdecimal.Decimal

// 使用时转换为fpdecimal.Decimal
myDec := MyDecimal(fpdecimal.FromInt(100))
stdDec := fpdecimal.Decimal(myDec)

批量运算

// 高性能批量加法
func sumDecimals(decimals []fpdecimal.Decimal) fpdecimal.Decimal {
    var sum fpdecimal.Decimal
    for _, d := range decimals {
        sum = sum.Add(d)
    }
    return sum
}

数据库集成

// GORM自定义类型
type Product struct {
    Price fpdecimal.Decimal `gorm:"type:decimal(20,6)"`
}

// 查询
var p Product
db.First(&p)
fmt.Println(p.Price)

性能优化建议

  1. 尽量复用Decimal变量而不是创建新变量
  2. 对于循环中的运算,考虑使用指针接收者方法
  3. 批量处理时预分配结果切片
  4. 避免频繁的Decimal与string/float64转换

与标准库对比

// 标准库math/big
import "math/big"

func bigExample() {
    a := big.NewRat(100, 1)
    b := big.NewRat(200, 1)
    result := new(big.Rat).Add(a, b)
    fmt.Println(result.FloatString(6))
}

// fpdecimal
func fpdecimalExample() {
    a := fpdecimal.FromInt(100)
    b := fpdecimal.FromInt(200)
    result := a.Add(b)
    fmt.Println(result)
}

fpdecimal相比标准库math/big有更简洁的API和更好的性能。

注意事项

  1. 除法运算需要检查错误(除零错误)
  2. 序列化时注意小数位精度
  3. 从float64创建可能会有精度损失
  4. 默认6位小数,超出部分会四舍五入

fpdecimal是处理金融计算等高精度需求的理想选择,它提供了简洁的API和优异的性能表现。

回到顶部