Java 中从 BigDecimal 到字节数组的舍入错误

Rounding error from BigDecimal to byte array in Java

如何正确处理 Java 中 BigDecimal 的转换舍入错误:

BigDecimal -> byte[] -> BigDecimal

我有一个长度为 32 字节的自定义数据类型(是的,32 字节而不是 32 位),我需要将 BigDecimal 的小数部分解码为 byte[]

我知道我会失去一些准确性。是否有任何成熟的技术来实现这种转换?

注意: 它是 MxN 形式的定点数据类型,其中 M % 8 == N % 8 == 0

您的定点小数部分可以解释为分数 n/(2[=45] 的分子 n =]256).因此,我建议计算表示 1/(2256) 的 BigDecimal 值(这完全可以表示为 BigDecimal)并将对它的引用存储在final static 字段。

要将 转换为 a byte[],然后,使用 BigDecimal.divideToIntegralValue() 的双参数版本将起始数字的小数部分除以 1 /(2256),使用MathContext参数指定你想要的舍入模式。大概您想要 RoundingMode.HALF_EVENRoundingMode.HALF_UP。然后通过 BigDecimal.unscaledValue() 得到结果的 BigInteger 未缩放值(它在数值上应该等于缩放后的值,因为整数值应该有缩放 0)。 BigInteger.toByteArray() 然后会给你一个 byte[] 与你所追求的密切相关。*

换句话说,您几乎可以逆转这个过程。 BigDecimal 有一个接受 byte[] 的构造函数,它再次与您的表示密切相关。使用该构造函数,将您的 byte[] 转换为 BigInteger,然后通过适当的构造函数将其转换为 BigDecimal。乘以存储的 1/(2256) 值得到你想要的小数部分。


* 这里最大的技巧可能是适当地摆弄标志。如果您的 BigDecimal 可能是负数,那么您可能希望在转换为 byte[] 之前先获取它们的绝对值。更重要的是,BigInteger 生产和消费的 byte[] 使用 补码 表示( 带符号位), 而我想你会想要一个无符号的纯二进制表示。这主要意味着您在转换时需要允许一个额外的位——因此是一个完整的额外字节。还要注意字节顺序;查看 BigInteger 的文档了解它使用的字节顺序,并适当调整。