Go Protobuf 精度小数

11

如果我想传输任意精度的十进制值,在我的protobuf定义文件中使用什么正确的标量类型

我在我的Go代码中使用shopspring/decimal而不是float64来防止数学错误。 当编写protobuf文件以便通过gRPC传输这些值时,我可以使用:

  • double,它转换为float64
  • string,它在自己的方式上肯定是精确的,但让我感觉很笨拙
  • 类似于mgravell / protobuf-net中的decimal

传统智慧告诉我在货币应用程序中避免使用浮点数,但我可能过于小心,因为这是序列化的一个要点。


我的 decimal 方法不是任意精度的;它只是我在 .NET 的 System.Decimal 中所需的。对于跨平台的任意精度,我可能会选择使用 string。而且,是的:绝对要避免在金融应用中使用 float/double - Marc Gravell
4
以64位无符号整数作为分/最小面额的金额,这通常是货币交易的方式。 - thwd
如果您的数字可能为负数(例如借方),则可以考虑使用sint32/64。 - Ravi R
@MarcGravell 感谢您的想法/反馈。想要写一个答案吗? - Matt Mc
添加足够多的十的幂,使舍入误差可以忽略不计。uint64非常大。 - thwd
显示剩余2条评论
1个回答

3
如果你真的需要任意精度, 目前我担心没有正确答案。有https://github.com/protocolbuffers/protobuf/issues/4406这个请求,但似乎不是很活跃。如果没有内置支持,你就需要手动执行序列化,然后使用stringbytes来存储结果。想要使用哪一个取决于是否需要跨平台/跨库兼容性:如果需要兼容性,使用string并在读取器中使用适当的任意精度类型解析字符串中的十进制表示;如果不需要,并且将使用相同的CPU架构和库来读取数据,那么您可能只需使用该库提供的二进制序列化(MarshalBinary/UnmarshalBinary)并使用bytes
另一方面,如果您只需要发送具有适当精度的货币值,并且不需要任意精度,则可能只需使用 sint64/uint64 并使用适当的单位(通常称为定点数)。例如,如果您需要以 4 位小数表示美元的货币价值,则您的单位将是 1 美分的 1/10000,因此例如值 1 表示 $0.0001,值 19900 表示 $1.99,值 -500000 表示 $-50 等。使用这样的单位,您可以表示范围为 $-922,337,203,685,477.5808 到 $922,337,203,685,477.5807 的值,这应该足够大多数目的。您仍然需要手动执行缩放,但应该非常简单和便携。考虑到上述范围,建议使用 sint64 更好,因为它还允许您表示负值;只有在需要额外的正值范围而不需要负值时才应考虑使用 uint64。

或者,如果您不介意导入另一个包,您可能想要查看https://github.com/googleapis/googleapis/blob/master/google/type/money.protohttps://github.com/googleapis/googleapis/blob/master/google/type/decimal.proto(恰好实现了与上述两个模型非常相似的内容),以及https://pkg.go.dev/github.com/googleapis/go-type-adapters/adapters中的相关实用函数。

顺便说一下,您完全正确,几乎永远不应该使用浮点数表示货币价值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接