Search code examples
jsonkotlinjacksondoublebigdecimal

BigDecimal to Double in order to add to JSON Object


How we convert BigDecimal into Double without losing precision in Kotlin?. I need to put it in a JSON response.

I'm using Vert.x and JsonObject. I've tried converting BigDecimal with scale 2 to Double with toDouble. Internally it uses Jackson as Object mapper

Example:

Currently:
BigDecimal("0.000") -> Response: { amount: 0.0 }

What I need:
BigDecimal("0.000") -> Response: { amount: 0.000 }


Solution

  • I'm afraid you can't convert a BigDecimal into a Double without losing precision, for several reasons:

    • There are many more possible values for BigDecimal than for Double, so the conversion is necessarily lossy.

    Doubles are 64-bit, so can't have more than 2⁶⁴ distinct values, while BigDecimals are effectively unlimited.

    • BigDecimals store decimal fractions, while Doubles store binary fractions.  There's very little overlap between the two, so in most cases the conversion will need to round the value.

    Both can store integers exactly (up to a certain value), and both can store fractions such as 0.5 exactly.  But nearly all decimal fractions can't be represented exactly as a binary fraction, and so for example there's no Double holding exactly 0.1.  (1/10 is an infinite recurring fraction in binary — 0.0001100110011… — and so no finite binary fraction can represent it exactly.)

    This means that in Kotlin (and most other programming languages), a numeric literal such as 0.1 gets converted to the nearest double-precision number, which is around 0.100000000000000005551115….  In practice, this is usually hidden from you, because when you print out a Double, the formatting routine will round it off, and in many cases that gives back the original number.  But not always, e.g.:

    >>> println(0.1 + 0.1 + 0.1)
    0.30000000000000004
    

    (All of this is discussed in other questions, most notably here.)

    • Unlike BigDecimals, Doubles have no precision, so they can't make the distinction you want anyway.

    For example, both 1.0 and 1.000000 are represented by exactly the same Double value:

    >>> println(1.000000)
    1.0
    

    I don't know Vert.x, but I'd be surprised if you really needed a Double here.  Have you tried using a BigDecimal directly?

    Or if that doesn't work, have you tried converting it to a String, which will preserve whatever formatting you want?