小智 9
这似乎是创建类型的绝佳机会,它以安全且精确的基于整数的方式存储值,但为您提供了您想要的十进制类型的额外行为。例如,快速实现可能如下所示(https://play.golang.org/p/nYbLiadQOc):
// USD represents US dollar amount in terms of cents
type USD int64
// ToUSD converts a float64 to USD
// e.g. 1.23 to $1.23, 1.345 to $1.35
func ToUSD(f float64) USD {
return USD((f * 100) + 0.5)
}
// Float64 converts a USD to float64
func (m USD) Float64() float64 {
x := float64(m)
x = x / 100
return x
}
// Multiply safely multiplies a USD value by a float64, rounding
// to the nearest cent.
func (m USD) Multiply(f float64) USD {
x := (float64(m) * f) + 0.5
return USD(x)
}
// String returns a formatted USD value
func (m USD) String() string {
x := float64(m)
x = x / 100
return fmt.Sprintf("$%.2f", x)
}
Run Code Online (Sandbox Code Playgroud)
给定类型的行为与人们预期的一样,特别是考虑到棘手的用例。
fmt.Println("Product costs $9.09. Tax is 9.75%.")
f := 9.09
t := 0.0975
ft := f * t
fmt.Printf("Floats: %.18f * %.18f = %.18f\n", f, t, ft)
u := ToUSD(9.09)
ut := u.Multiply(t)
fmt.Printf("USD: %v * %v = %v\n", u, t, ut)
Run Code Online (Sandbox Code Playgroud)
产品售价 9.09 美元。税率为 9.75%。
浮点数:9.089999999999999858 * 0.097500000000000003 = 0.886275000000000035
美元:9.09 美元 * 0.0975 = 0.89 美元
我想说一种方法是使用适当大小的整数类型来存储金额,并将其标准化为可能的最低金额.比如说,如果您需要以美元存储金额低至1美分,请将您的价值乘以100,然后将它们存储在全部美分中.
另一种方法是实现一个自定义类型,它可以模拟某些其他语言中的"十进制",也就是说,它会使用两个整数来表示金额.
有理数是表示货币价值的一个很好的解决方案。即,具有分子和分母的类型。
通常货币数据结构过于复杂——Java 的 BigDecimal 就是一个例子。一种在数学上更一致的方法是定义一种处理有理数的类型。当使用 64 位整数时,可以准确有效地表示大量数字。与需要将二进制分数转换为/从十进制分数转换的任何解决方案相比,错误和舍入问题都不是问题。
编辑:Go 标准库包括任意精度整数和有理数。Rat 类型适用于货币,特别是对于那些需要任意精度的情况,例如外汇。这是一个例子。
编辑 2:我广泛使用了decimal.Decimal Shopspring 软件包。在幕后,这big.Int与指数相结合以提供具有几乎无限范围值的定点小数。该Decimal类型是有理数分母始终是10的幂,这在实践中工作得非常好。