背景
最近在写go,遇到一个问题,go没有自带的decimal类型,只有一个math/big用于大数运算。遇到购物支付的场景,这时候使用浮点数计算非常有必要,而且通常都是精确到小数点后两位。
查阅了一些资料,找到了一个start数比较高的解决方案。
shopspring/decimal: github.com/shopspring/…
文档地址: pkg.go.dev/github.com/…
需要注意的是: Decimal库“只能”表示小数点后最多 2^31 位的数字。但是这已经足够满足我们目前的需求。
它的原理是使用十进制定点数表示法,有多少位小数就小数点后移多少位,value保存移之后的整数,exp保存小数点后的数位个数,number=value*10^exp,因为移小数点后的整数可能很大,所以这里借用标准包里的math/big
表示这个大整数。exp使用了int32,这也就是为什么Decimal库最多“只能”表示小数点后最多 2^31 位的数字的原因。
以下是源码中Decimal的定义
type Decimal struct {
value *big.Int
// NOTE(vadim): this must be an int32, because we cast it to float64 during
// calculations. If exp is 64 bit, we might lose precision.
// If we cared about being able to represent every possible decimal, we
// could make exp a *big.Int but it would hurt performance and numbers
// like that are unrealistic.
exp int32
}
复制代码
示例
使用前提: Go version >=1.7
go get github.com/shopspring/decimal
复制代码
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
// 将string类型转换为decimal类型
price, err := decimal.NewFromString("136.02")
if err != nil {
panic(err)
}
// 支持负数
n, err := decimal.NewFromString("-123.4567")
n.String() // output: "-123.4567"
// 将int类型转为decimal类型
quantity := decimal.NewFromInt(3)
// 整数部门为空也是可以正常转换的
fee, _ := decimal.NewFromString(".035")
taxRate, _ := decimal.NewFromString(".08875")
// 乘
subtotal := price.Mul(quantity)
// 先加再乘
preTax := subtotal.Mul(fee.Add(decimal.NewFromFloat(1)))
total := preTax.Mul(taxRate.Add(decimal.NewFromFloat(1)))
fmt.Println("Subtotal:", subtotal) // Subtotal: 408.06
fmt.Println("Pre-tax:", preTax) // Pre-tax: 422.3421
fmt.Println("Taxes:", total.Sub(preTax)) // Taxes: 37.482861375
fmt.Println("Total:", total) // Total: 459.824961375
fmt.Println("Tax rate:", total.Sub(preTax).Div(preTax)) // Tax rate: 0.08875
// 支持科学计数法
fmt.Println(NewFromFloat(-1e13).String()) // output: "-10000000000000"
// 除,默认是没有小数点后的精确位数
d1 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3))
d1.String() // output: "0.6666666666666667"
// 小数点后保留三位
decimal.DivisionPrecision = 3
d4 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3))
d4.String() // output: "0.667"
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END