How should one properly handle a conversion rounding error with BigDecimal in Java:
BigDecimal -> byte[] -> BigDecimal
I have a custom datatype 32 bytes in length (yep, 32 bytes not 32 bits) and I need to decode the fractional part of BigDecimal
into byte[]
.
I understand that I will lose some accuracy. Are there any established techniques to implement such a conversion?
NOTE:
It is fixed point datatype of form MxN
, where M % 8 == N % 8 == 0
Your fixed-point fractional part can be interpreted as the numerator, n, of a fraction n/(2256). I suggest, therefore, computing the
BigDecimal
value representing 1/(2256) (this is exactly representable as aBigDecimal
) and storing a reference to it in afinal static
field.To convert to a
byte[]
, then, use the two-arg version ofBigDecimal.divideToIntegralValue()
to divide the fractional part of your starting number by 1/(2256), using theMathContext
argument to specify the rounding mode you want. Presumably you want eitherRoundingMode.HALF_EVEN
orRoundingMode.HALF_UP
. Then get theBigInteger
unscaled value of the result (which should be numerically equal to the scaled value, since an integral value should have scale 0) viaBigDecimal.unscaledValue()
.BigInteger.toByteArray()
will then give you abyte[]
closely related to what you're after.*To go the other way, you can pretty much reverse the process.
BigDecimal
has a constructor that accepts abyte[]
that, again, is very closely related to your representation. Using that constructor, convert yourbyte[]
to aBigInteger
, and thence toBigDecimal
via the appropriate constructor. Multiply by that stored 1/(2256) value to get the fractional part you want.* The biggest trick here may involve twiddling signs appropriately. If your
BigDecimal
s may be negative, then you probably want to first obtain their absolute values before converting tobyte[]
. More importantly, thebyte[]
s produced and consumed byBigInteger
use a two's complement representation (i.e. with a sign bit), whereas I suppose you'll want an unsigned, pure binary representation. That mainly means that you'll need to allow for an extra bit -- and therefore a whole extra byte -- when you convert. Be also aware of byte order; check the docs ofBigInteger
for the byte order it uses, and adjust as appropriate.