Golang中如何安全且精确地表示金额?
Golang中如何安全且精确地表示金额? 针对这个问题,我在这里找到了一些信息:
What is the proper golang equivalent to Decimal when dealing with money? Getting Help
有各种浮点数类型,包括 big 包中的新类型。但“传统”智慧告诉我们,在尝试表示货币时,应始终避免使用浮点数。 在 Go 中,正确的解决方案是什么? 当然,你可以使用 Rational 结构体(Rat)以分数的形式表示十进制值,你可以用它来将科学计数法等解析为有理数表示。 但在 Go 中处理货币的“正确”方式是什么?
我的问题是:我需要存储精确到小数点后两位的货币金额。 以整分(int64)表示似乎是一个选项。有人能分享一下在 Go 中处理货币金额是否有其他方法吗?也许,有可靠的数据类型替代方案。 谢谢
更多关于Golang中如何安全且精确地表示金额?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
感谢提供的线索。我会研究一下,看看效果如何。
更多关于Golang中如何安全且精确地表示金额?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
感谢回复。 我明白了,是的,浮点数不是一个好的选择,尤其是在涉及较大交易的情况下。 我正在寻找一些比使用整数更好的替代方案。
货币必须使用整数类型存储。使用浮点类型在长时间运行后可能导致精度损失,从而损失几分钱。
免责声明:我尚未在货币计算中使用过Go。我还没有深入思考过这个问题。
是否可以考虑像time.Duration的实现方式那样来处理货币?类似这样:https://play.golang.org/p/Woe8FnVz6LE
package main
import (
"fmt"
)
type Currency int64
const (
Cent Currency = 1
Dollar = 100 * Cent
)
func (c Currency) Dollars() float64 {
return float64(c) / float64(Dollar)
}
func (c Currency) String() string {
return fmt.Sprintf("$%.2f", c.Dollars())
}
func main() {
var price Currency = 2*Dollar + 49*Cent
fmt.Println(price)
}
在Go中处理货币金额,推荐使用整数类型(如int64)以最小单位(如分)存储,这是最安全且精确的方法。以下是一个示例实现:
package main
import (
"fmt"
)
// Money 表示货币金额,单位为分
type Money int64
// NewMoney 从元和分创建Money
func NewMoney(yuan, fen int64) Money {
return Money(yuan*100 + fen)
}
// Yuan 返回元的整数部分
func (m Money) Yuan() int64 {
return int64(m) / 100
}
// Fen 返回分的整数部分
func (m Money) Fen() int64 {
return int64(m) % 100
}
// String 返回格式化字符串
func (m Money) String() string {
return fmt.Sprintf("%d.%02d", m.Yuan(), m.Fen())
}
// Add 加法运算
func (m Money) Add(other Money) Money {
return m + other
}
// Sub 减法运算
func (m Money) Sub(other Money) Money {
return m - other
}
// Mul 乘法运算(与整数相乘)
func (m Money) Mul(multiplier int64) Money {
return m * Money(multiplier)
}
// Div 除法运算(除以整数)
func (m Money) Div(divisor int64) Money {
return m / Money(divisor)
}
func main() {
// 创建金额:123.45元
m1 := NewMoney(123, 45)
fmt.Printf("金额1: %s\n", m1) // 输出: 金额1: 123.45
// 创建金额:67.89元
m2 := NewMoney(67, 89)
fmt.Printf("金额2: %s\n", m2) // 输出: 金额2: 67.89
// 加法
sum := m1.Add(m2)
fmt.Printf("总和: %s\n", sum) // 输出: 总和: 191.34
// 减法
diff := m1.Sub(m2)
fmt.Printf("差额: %s\n", diff) // 输出: 差额: 55.56
// 乘法(例如计算3倍金额)
multiplied := m1.Mul(3)
fmt.Printf("3倍金额: %s\n", multiplied) // 输出: 3倍金额: 370.35
// 除法(例如平均分成2份)
divided := m1.Div(2)
fmt.Printf("一半金额: %s\n", divided) // 输出: 一半金额: 61.72
}
对于需要更高精度或复杂小数运算的场景,可以使用github.com/shopspring/decimal库:
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
// 创建精确十进制金额
price, _ := decimal.NewFromString("123.456789")
// 精确计算
quantity := decimal.NewFromInt(3)
total := price.Mul(quantity)
// 四舍五入到2位小数
rounded := total.Round(2)
fmt.Printf("单价: %s\n", price) // 输出: 单价: 123.456789
fmt.Printf("总价: %s\n", total) // 输出: 总价: 370.370367
fmt.Printf("舍入后: %s\n", rounded) // 输出: 舍入后: 370.37
}
整数表示法避免了浮点数精度问题,而decimal库提供了更灵活的十进制运算。选择取决于具体需求:简单金额计算用整数类型,复杂金融计算用decimal库。

