在Go中使用接口:实用性与脆弱性的平衡货币的精度
在Go中使用接口:实用性与脆弱性的平衡
点击上方“名片”,关注公众号,加群可获得我整理的海量资料
在开发的初始阶段,我们经常会遇到“浮点数精度”和“货币值表示”的问题。
那么,如何处理货币,如何存储和传递它们。
为什么是问题?
Go语言中的标准浮点类型具有一定的精度(像其他任何语言一样),你不能在货币操作中使用它们。这里有一个最简单的例子:
var v1, v2 = 0.1, 0.2
fmt.Println(v1 + v2)
// 输出:0.30000000000000004
你可以计算你需要将一个值与另一个值相加多少次,才能在你的账户上获得额外的钱!但反过来也是一样 — 在这种情况下,你只是失去了你的钱。
这不仅在对你的钱进行数学运算时有问题,而且在不同系统或服务之间传递数据时也是有问题的。
下一个问题 — 传递你的钱
每次将你的钱从/到浮点数进行编组时,都会遇到与上述相同的问题,以及与编组器实现有关的其他问题 - json,xml,text等等...
另一个问题是四舍五入。如果你处理的是货币,你总会面临四舍五入的问题。你应该如何四舍五入你的货币值?例如 0.345 元,一般我们还是会四舍五入到 0.35 元?
我们的选择是什么?
有一些特殊的类型可用于货币的表示和计算。
Go标准库有 big.Float
类型(来自 math/big
包,表示任意精度的浮点数)。与 float32
和 float64
不同,它们具有固定的大小和精度,big.Float
允许你为数字和计算设置任意精度。
另一个不错的选择是 decimal
库 (https://github.com/shopspring/decimal)。
关于四舍五入:
- 1.234 => 1.23
- 1.235 => 1.24
- 1.236 => 1.24
例如,shopspring/decimal
提供了适当舍入值的方法。
考虑的另一个好选择是使用货币单位。这样,你就从浮点数问题转移到整数,并将一切都作为整数计算。在这里唯一使用四舍五入的地方:传递结果值。
现在让我们讨论一下在传递货币时的选择。
- 使用货币单位 — 我们将所有内容都传递为整数,这里没有浮点问题。只需控制值的限制,就可以了。
- 将浮点数作为字符串传递。通常也是一个不错的选择 — 当你将浮点数作为字符串传递时,带有所需精度(特定小数位数)的字符串,当对方读取此字符串值并将其转换回浮点数时,你就是安全的。
简单的例子
你可以在 Go Playground 上尝试一下。
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
func main() {
a := 0.1
b := 0.2
c := decimal.NewFromFloat(a)
d := decimal.NewFromFloat(b)
fmt.Println(a, b, c.String(), d.String())
fmt.Println(a + b)
fmt.Println(c.Add(d).String())
}
输出为:
0.1 0.2 0.1 0.2
0.30000000000000004
0.3
结论
处理货币时 — 使用 math/big
或一些与货币相关的库,比如 shopspring/decimal
,或者只是使用货币单位,在这里不要使用浮点数。将货币作为字符串传递,或者在货币单位中传递,不要在这里使用浮点数。
其实还有一个小技巧:对于这些数值,我们可以使用:xxx * 10000
的方式,这样我们就可以保留其精度。10000
这个值可以在团队内协商。
如果你也认可,请为它点赞。
也欢迎大家加入我创建技术交流群,交个朋友~
在交流群里,有问题可以咨询我哈,我也会分享技术、搞钱等信息,一起加油~
👇当然你也可以加入我创建的副业陪伴星球!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期
· 全程使用 AI 从 0 到 1 写了个小工具
· 从文本到图像:SSE 如何助力 AI 内容实时呈现?(Typescript篇)