go - Go Protobuf 精确小数
问题描述
如果我想传输任意精度的十进制值,在我的 protobuf 定义文件中使用的正确标量类型是什么?
我在 Go 代码中使用shopspring/decimal而不是 float64 来防止数学错误。在编写我的 protobuf 文件以通过 gRPC 传输这些值时,我可以使用:
double
转换为 float64string
这肯定会以自己的方式精确,但让我觉得笨重- 来自mgravell/protobuf-net 的十进制之类的东西?
传统智慧教会我在货币应用中避开浮动,但我可能过于小心,因为它是序列化的一个点。
解决方案
如果您真的需要任意精度,我担心现在没有正确的答案。有https://github.com/protocolbuffers/protobuf/issues/4406打开,但是好像不是很活跃。如果没有内置支持,您确实需要手动执行序列化,然后使用其中一个string
或bytes
存储结果。使用哪一个可能取决于您是否需要跨平台/跨库兼容性:如果您需要兼容性,请string
使用bytes
string
并在阅读器中使用适当的任意精度类型解析字符串中的十进制表示;如果您不需要它并且您将使用相同的 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.proto或https://github。 com/googleapis/googleapis/blob/master/google/type/decimal.proto(顺便实现了与上述两个模型非常相似的东西),以及https://pkg.go.dev/github 上的相关实用程序函数。 com/googleapis/go-type-adapters/适配器
作为旁注,您完全正确,您几乎不应该将浮点用于货币值。
推荐阅读
- html - Youtube 视频在 iframe 标记中给出,但在 Safari 浏览器中不起作用
- javascript - 关于 TypeScript 字符串文字类型的 TSDocs
- excel-formula - 如何在 Excel 公式中使用包含负数和正数的条件的 IF 函数
- sql - 对行进行分组,使每列的总和不超过 10
- inno-setup - 从注册表中查找现有程序路径
- c++ - 如何通过opencv计算图像中两个圆的距离
- serialization - Avro 的优缺点 - JSON / 二进制序列化
- go - 测试并发映射读取和映射写入
- reactjs - 道具类型失败:提供给“输入”的值“搜索”的道具“类型”无效
- angular7 - 如何从 ASP.NET Core 调用存储过程并在 Angular 7 中提供服务?需要逐步解决方案