jackson-databind icon indicating copy to clipboard operation
jackson-databind copied to clipboard

Serialize `BigDecimal` as a string instead of a floating-point number.

Open garretwilson opened this issue 4 months ago • 15 comments

Search before asking

  • [x] I searched in the issues and found nothing similar.

Describe the bug

I've discovered that Jackson serializes Java BigDecimal values as JSON numbers rather than strings. I can work around this in my own code, but I wanted to bring it to your attention as I consider this a serious issue that directly contradicts the explicit desire of the developer.

JavaScript doesn't indicate (or at least didn't use to) how the "number" type is represented, but the vast majority of implementations across platforms use IEEE 754. This standard uses base 2 to represent the fractional part. This means that there are many values that cannot be represented but only estimated. (This is not for lack of precision, but rather because of the number base used.)

Most currencies on the other hand use base 10 for representing fractional parts. Likewise there are many fractions that base 10 cannot represent, but the significant point here is that the unrepresentable values differ based upon the base. In other words, using floating point numbers guarantees that there are many values that can only be estimated that would have been accurately represented in base 10.

For this reason it is a well-known recommendation for decades not to use IEEE 754 to represent currency. See e.g. Be cool. Don’t use float/double for storing monetary values. A good explanation is in this Stack Overflow answer, but there are surely dozens of articles that have already covered this in depth.

I stress that this has nothing to do with precision. We could have a 128-bit or 256-bit precision or whatever, but if we are using the base 2 fractions that IEEE 754 uses, even a simple value such as $0.10 (10 cents, in whatever currency) will only be represented as an estimate in IEEE 754.

In short, if a Java developer has used BigDecimal, they have expressly indicated that their application, for whatever reason, needs to accurately represent fractional parts using base 10 (hence the "decimal" in BigDecimal). Converting the decimal value to what in all practical cases will surely be represented as an IEEE 754 floating point number, representing fractional parts using base 2, is guaranteed to lose information and directly goes against the express wish of the developer.

Version Information

No response

Reproduction

<-- Any of the following

  1. Brief code sample/snippet: include here in preformatted/code section
  2. Longer example stored somewhere else (diff repo, snippet), add a link
  3. Textual explanation: include here -->
// Your code here

Expected behavior

No response

Additional context

No response

garretwilson avatar Jun 16 '25 14:06 garretwilson