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设计的高性能固定小数位精确序列化与算术运算库,特别适合金融、会计等需要高精度计算的场景。
主要特性
- 固定小数位数(默认6位)
- 高性能算术运算
- 精确序列化/反序列化
- 零内存分配设计
- 线程安全
安装
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)
性能优化建议
- 尽量复用Decimal变量而不是创建新变量
- 对于循环中的运算,考虑使用指针接收者方法
- 批量处理时预分配结果切片
- 避免频繁的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和更好的性能。
注意事项
- 除法运算需要检查错误(除零错误)
- 序列化时注意小数位精度
- 从float64创建可能会有精度损失
- 默认6位小数,超出部分会四舍五入
fpdecimal是处理金融计算等高精度需求的理想选择,它提供了简洁的API和优异的性能表现。