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

5 回复

感谢提供的线索。我会研究一下,看看效果如何。

更多关于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库。

回到顶部