首页 > 解决方案 > Go Protobuf 精确小数

问题描述

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

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

传统智慧教会我在货币应用中避开浮动,但我可能过于小心,因为它是序列化的一个点。

标签: goprotocol-buffersarbitrary-precision

解决方案


如果您真的需要任意精度,我担心现在没有正确的答案。有https://github.com/protocolbuffers/protobuf/issues/4406打开,但是好像不是很活跃。如果没有内置支持,您确实需要手动执行序列化,然后使用其中一个stringbytes存储结果。使用哪一个可能取决于您是否需要跨平台/跨库兼容性:如果您需要兼容性,请string使用bytesstring并在阅读器中使用适当的任意精度类型解析字符串中的十进制表示;如果您不需要它并且您将使用相同的 cpu 架构和库来读取数据,您可能只需使用该库 ( MarshalBinary/ UnmarshalBinary) 提供的二进制序列化并使用bytes.

另一方面,如果您只需要以适当的精度发送货币值而不需要任意精度,您可能只需使用sint64/uint64并使用适当的单位(这些通常称为定点数)。举个例子,如果您需要用 4 位小数表示以美元为单位的货币价值,您的单位将是美元的 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/适配器

作为旁注,您完全正确,您几乎不应该将浮点用于货币值。


推荐阅读