在 Go 项目里,涉及到金额计算,大家一般用什么方式?
以分为单位存储,然后程序里都使用整形来计算
以元到单位存储,使用 math/big 包处理高精度金额
分为单位,相应国家法规
#1
纠正,这叫辅币单位…
计算根据精度要求统一转成整形来计算,存储正常是分
第一种
第二种用法也不应该用 math/big ,那还是二进制的。而是应该用 Decimal ,比如: pkg.go.dev/github.com/shopspring/decimal
金额计算最重要的应该是 尽可能地延后计算(尤其是涉及乘除法的)来最大化避免误差。某些时候表达式(例如字符串 "10.0/3*3")可能是交换数据时更好的表示方式。
正常用第一种,分;如果无奈必须要用第二种,用 shopspring/decimal 库
元*1e6 存 bigint
最早的时候用 vb 实现的是,先转换为整数,再计算,算完再转为小数。
另外需要看场景,往往和钱有关系的系统,通常也和财务有关系,和财务有关系的通常和“成本核算”有关系。
此时,用“分”为单位反而不适合,因为涉及“成本”的计算,就要很多位小数了。
说回来,如果只是涉及“钱”,那未来也可能有多币种问题,还是绕不开多位小数。
所以为什么不一劳永逸呢。
分三个字段,整数(bigint)、小数(bigint)、小数位数(int)
上家公司数据库里存的是字符串,计算的话用了 decimal 库,小数点后保留 8 位,计算完后转字符串后存到数据库
这是个业务或者说工程问题,不全是技术问题。因为不像 Java 用 BigDecimal 比较多,Go 这边没有习惯性做法,所以主要靠约定,剩下的是如何执行。无论哪种语言,计算这类纯技术问题都是小事,与业务结合的部分才是大事。
我这里说几个技术人员可能不太熟悉的业务需求或者是需要注意的点:
- 最理想的是用 Go 自定义数据结构,在数据结构上实现相应的运算方法。这样可以避免代码上的出错。这里比较关键的点是,计算过程肯定是高精度/整型(对应 BigDecimal ),同时一定要支持 split 操作,主要是处理类似 1 拆成 0.33+0.33+0.34 的业务需求,这种在会计、税务场景很常见。
- 根据全流程数据交换的环节来定义精度和数据格式。一般推荐的是比常见货币最小单位更精确一点,或者支持自定义。传递过程中使用字符串,做好格式转换。说到底还是靠约定,为了方便执行可以独立成库强制所有上下游都使用特定实现。
- 有需要的话提前考虑多币种支持,比如订单系统如果可以支持多币种,数据库和业务逻辑都要做 schema 调整。通常的业务需求来说,多币种就是多账本,如果最初设计成单账本后期修改会很痛苦。
推荐使用 event driven 架构来做 cqrs ,即用事务的方式持久化变动,实时状态由历史计算推导得出。类似银行卡只记录进出,余额是算出来的。好处一方面能和通常的消息队列架构方便对接,另一方面整个业务的可靠性、灾难恢复都好做。有 olap 需求也容易改造。
以最小单位存储
比如日元就没有分
美元有分
人民币有分minor unit
自己做一个指定 decimal 的定点数类吧
我有个问题,哪怕用分为单位算 如果除完之后 是 6666.6666666 这到底是直接舍弃小数点后面的 还是进一位?
用的 5 楼的那个库
非金融行业的看领导决定咯,满五进一还是直接舍弃
10 分分成 3 份,那就是 3+3+4 ,最后一份是差值出来的。至于用 3 还是 4 ,要业务方来决定
用分计算的目的就是为了方便直接舍弃
衍生问题:写 web API 时,JSON 里表示金额用什么类型?
字符串
不是特殊金融行业 一律分
金融行业也争取用小数点后八位 int64我的回答可能和具体语言无关。
- 建议采用对应货币最小单位作为存储,主要是为了避免在计算和存储过程中产生精度损失导致资损;
- 不管是 java 还是 go ,请编码统一的金额处理工具包,定义好金额结构和转换方法,避免直接使用基础结构例如整形,浮点来表示金额, 所有金额的表示直接使用定义好的金额结构。 整个项目内部统一采用,保证金额计算、转换的准确性。
- 一个金额处理工具包应该考虑如下内容:
3.1. 要能够进行序列化和比较
3.2 要能存储币种和金额,要确保高精度和标准化
3.3 要能提供丰富的操作方法,包括加减乘除,需要自定义舍入和四舍五入;
3.4 要提供常用单位 getAmount()和获取最小单位金额的方法 getAmountMinorUnit()
3.5 确保结构的不可变性和安全性;
3.6 需要能够根据币种计算出最小单位,例如人民币就是分,日元就是元;
3.7 乘除支持舍入,默认使用四舍五入,但是支持其它舍入方式。
以上可以在跨境支付、金融应用等需要高精度、多币种支持的场景种安全有效的进行金额处理。
c#里用 decimal 类型,十进制浮点
pkg.go.dev/github.com/shopspring/decimal
用这个,它同时支持 MYSQL
业务决定,不同场景不同处理方式,可能都会有。
在前公司用 math/big 并封装成一 struct
本来想用 decimal 但搞计算的用 math/big 就算了
理由是精度够高了
至于延后计算可能要另外找库或自己刻 没特别研究过 肯定是种专业的数学库
bigint + decimal 加密货币小数点后 18 位都能算
so ?你要用毛吗?哈哈哈哈哈哈
这个老哥可以
除了 blockchain 动辄多少个 0 的大范围数值,其他普通领域 int64 保平安足够了
某鹅某部门就是分为单位,int64
这个库作者好像没空维护了
反正别出现浮点数就行了
这个有什么鸡毛讨论的,普通货币有用 2 的大聪明出来说说?
讲细一点 我跟着用而已
#15
正常来说财务账目上没有除法运算 设计上最后平衡都是加减(乘)
除法无论怎么算都是不影响平账的
如果除法能影响平账 那就是设计逻辑有问题 架构开除就好了
细思了一下 其实这问题调用系统的 bc 命令也是个解法 还实现延后计算的问题 如果有 golang 版做成 lib 的 bc 会更好
#10 记得巴菲特的公司曾经一天来回交易的流水把 8 位小数级别的误差给弄到小数点前面去了
事实上 bc 是种语言 也有 function 可以用 任意精度到 2147483647 位
zh.m.wikipedia.org/zh-cn/Bc%E8%AF%AD%E
命令行 cp 值真的太高
第一种,如果要用第二种,一定要用 decimal
学到了, 儿豁
有个算法叫银行家算法,6666.6666666 这到底是直接舍弃小数点后面的 还是进一位
发错了,根据银行家算法应该是进一位
啊?只用 rmb 就分呗
上面有人提到银行家算法。我想请教一下,银行家算法是通用做法吗?还是一种可选项?曾经被一个空降的 CTO 无情嘲讽,原话是『我从没见过一个电商平台使用四舍五入』
我看有的 NAS 都做到 500 多一个整体了(比如海康威视 mage20plus )才 551 块钱,支持几乎所有 NAS 的功能,为什么还会有人花 500 多买个 N100…
目前主要是通过小而美来接收,之前的企业机器人现在新注册的行不通了,又不太想下载专门的应用后台保活来收通知,只有 pushplus 和 wechatpusher 这类解决方案了吗…
我的 Mac 软件通过鼠标移动至刘海位置显示文件/文件夹来更方便的快速获取文件,这个想法和交互我想了很久,当然我也获得了回报,开发出来后受到不少国内外用户的喜欢. 这是我软件的…