Representing amounts in financial API's JSON

2024 Nov 04  |  3 min read  |  tags: api-design 1 json 1

The problem

JSON only supports float values as per the IEEE-754 standard. And IEEE 754 standard is terrible for representing most floats.

Here is a classic example:

>> 0.1+0.2
0.30000000000000004

The fundamental reason is simple - financial numbers are in powers of 10 (decimal), computer-stored numbers are in powers of 2 (binary). Powers of 2 cannot be used to accurately represent powers of 10, so there is always some rounding and some inaccuracy.

As a rule of thumb, don't use JSON to send fractional values. You can use JSON for floats only if:

  • You exactly know the limitations of IEEE-754 standard
  • You are completely sure that your use case is okay with the limitations

For financial APIs, IEEE-754 is not at all okay. The standard cannot represent fractional values properly. So, it cannot represent fractional amounts like $0.1. And your amounts are going to be fractional most of the time

Solution

The solution is simple - get rid of the float data type from your JSON, and only use integer or string datatypes in the JSON-communication layer.

Look at the financial APIs around you. They are all using integer or string in their JSOn.

  • Stripe, Razorpay use integer to represent amounts in their API
  • PayPal uses string to represent amounts in its API

In case you find float in a financial API, you're looking at people who truly don't know what they are doing, or you're looking at people who have truly given up on engineering.

HOW TO DO IT?

Converting the float into an integer is almost an industry standard. To do it, represent the amount in its currency's lowest denomination. Examples:

  • Smallest divisible unit of a rupee is a paisa. So, ₹22.33 becomes 2233 paise.
  • Smallest divisible unit of a dollar is a cent. So, $11.22 becomes 1122 cents.

Convert everything into int or string during communication, to prevent loss of information due to limited precision of the IEEE-754 float (int is preferred). Then, after receiving the JSON, convert the amount back into whatever datatype within your programs.


If you are wondering what is needed for accurately processing and storing financial numbers, or fractions in general , hare are 2 solutions:

  • During progrssing, you can use datatypes with arbitrary (unlimited) precision. For example, java BigInteger.
  • During storage, use datatypes that support fixed decimal values. For example:
    • SQLServer can use datatypes with definable precision like decimal,numeric.
    • Postgres also has numeric/decimal where you define the scale. Postgres also has a money datatype, but ironically, it is not recommended. (link :pg - don't use money).

And from experience, and after talking to too many people about this during the 2021-2022 period, just store money as integer in the smallest denomination. If you really need floats, decimal datatype with 4 decimal digits will solve most use cases (the internet favourite is DECIMAL(19, 4)).

And on an ending note, IEEE 754 is the reason why I cannot fathom why there are companies writing financial APIs in javascript (Node JS). That damn language has only one numeric datatype - double precision float (IEEE 754). Doesn't even have int!

Table of Contents: