购买/财务计算的Go类型

huangapple go评论85阅读模式
英文:

Go type for purchasing/financial calculations

问题

我正在使用Go语言构建一个在线商店。正如预期的那样,有几个重要的部分需要记录精确的货币金额。我知道浮点数存在舍入问题(例如,0.3无法精确表示等等)。

货币的概念似乎很容易只表示为一个字符串。然而,我不确定用什么类型来表示实际的货币金额最合适。

关键要求似乎是:

  • 可以精确表示小数,根据货币可以指定小数位数(一些货币使用超过2位小数:http://www.londonfx.co.uk/ccylist.html)
  • 显然需要基本的算术运算-加法/减法/乘法/除法。
  • 合理的字符串转换-这基本上意味着转换为其十进制等价物。此外,国际化至少应该是可能的,即使并没有为此构建所有逻辑(例如,在欧洲是1.000,在美国是1,000)。
  • 舍入,可能使用其他舍入方案,如银行家舍入。
  • 需要有一种简单明显的方式与数据库值对应-在我的情况下是MySQL。(在这个层面上,将值视为字符串可能是最合理的选择,以确保其值被完全保留。)

我知道math/big.Rat,它似乎解决了很多这些问题,但是例如它的字符串输出不能直接使用,因为它会以"a/b"的形式输出。我相信也有解决这个问题的方法,但我想知道是否有一些现有的最佳实践(我没有轻易找到)适用于这种情况。

更新:这个包看起来很有希望:https://code.google.com/p/godec/

英文:

I'm building an online store in Go. As would be expected, several important pieces need to record exact monetary amounts. I'm aware of the rounding problems associated with floats (i.e. 0.3 cannot be exactly represented, etc.).

The concept of currency seems easy to just represent as a string. However, I'm unsure of what type would be most appropriate to express the actual monetary amount in.

The key requirements would seem to be:

  • Can exactly express decimal numbers down to a specified number of decimal places, based on the currency (some currencies use more than 2 decimal places: http://www.londonfx.co.uk/ccylist.html )
  • Obviously basic arithmetic operations are needed - add/sub/mul/div.
  • Sane string conversion - which would essentially mean conversion to it's decimal equivalent. Also, internationalization would need to be at least possible, even if all of the logic for that isn't built in (1.000 in Europe vs 1,000 in the US).
  • Rounding, possibly using alternate rounding schemes like Banker's rounding.
  • Needs to have a simple and obvious way to correspond to a database value - MySQL in my case. (Might make the most sense to treat the value as a string at this level in order to ensure it's value is preserved exactly.)

I'm aware of math/big.Rat and it seems to solve a lot of these things, but for example it's string output won't work as-is, since it will output in the "a/b" form. I'm sure there is a solution for that too, but I'm wondering if there is some sort of existing best practice that I'm not aware of (couldn't easily find) for this sort of thing.

UPDATE: This package looks promising: https://code.google.com/p/godec/

答案1

得分: 5

你应该将国际化(i18n)与货币实现解耦。因此,不要将所有内容捆绑在一个结构体中并称之为完成。只标记金额表示的货币,不要做更多的操作。让国际化负责格式化、字符串化、添加前缀等操作。

使用任意精度的数值类型,比如math/big.Rat。如果这不是一个选项(因为序列化限制或其他障碍),那么使用最大的固定大小整数类型来表示所代表的货币中的原子金额——美元的分,日元的日元,瑞士法郎的拉班,欧元的分,等等。

当使用第二种方法时,要特别注意不要发生溢出,并定义明确而有意义的除法舍入行为。

英文:

You should keep i18n decoupled from your currency implementation. So no, don't bundle everything in a struct and call it a day. Mark what currency the amount represents but nothing more. Let i18n take care of formatting, stringifying, prefixing, etc.

Use an arbitrary precision numerical type like math/big.Rat. If that is not an option (because of serialization limitations or other barriers), then use the biggest fixed-size integer type you can use to represent the amount of atomic money in whatever currency you are representing – cents for USD, yens for JPY, rappen for CHF, cents for EUR, and so forth.

When using the second approach take extra care to not incur in overflows and define a clear and meaningful rounding behaviour for division.

huangapple
  • 本文由 发表于 2014年9月24日 11:22:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/26008004.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定