go 解决货币计算的难题:避免浮点数陷阱

TimLiuDream · · 302 次点击 · 开始浏览    置顶

> 关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力! ![](https://files.mdnice.com/user/38913/9ba43b8c-e69e-4dd4-9a34-9dddbf0eb58a.png) 在开发的初始阶段,我们经常会遇到“浮点数精度”和“货币值表示”的问题。 那么,如何处理货币,如何存储和传递它们。 ### 为什么是问题? 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](https://go.dev/play/p/D_v979phga1) 上尝试一下。 ```go 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()) } ``` 输出为: ```jsx 0.1 0.2 0.1 0.2 0.30000000000000004 0.3 ``` ### 结论 处理货币时 — 使用 `math/big` 或一些与货币相关的库,比如 `shopspring/decimal`,或者只是使用货币单位,在这里不要使用浮点数。将货币作为字符串传递,或者在货币单位中传递,不要在这里使用浮点数。 **其实还有一个小技巧:对于这些数值,我们可以使用:`xxx * 10000` 的方式,这样我们就可以保留其精度。`10000` 这个值可以在团队内协商。**

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

302 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传