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
BigDecimalvalue representing 1/(2256) (this is exactly representable as aBigDecimal) and storing a reference to it in afinal staticfield.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 theMathContextargument to specify the rounding mode you want. Presumably you want eitherRoundingMode.HALF_EVENorRoundingMode.HALF_UP. Then get theBigIntegerunscaled 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.
BigDecimalhas a constructor that accepts abyte[]that, again, is very closely related to your representation. Using that constructor, convert yourbyte[]to aBigInteger, and thence toBigDecimalvia 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
BigDecimals may be negative, then you probably want to first obtain their absolute values before converting tobyte[]. More importantly, thebyte[]s produced and consumed byBigIntegeruse 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 ofBigIntegerfor the byte order it uses, and adjust as appropriate.