In Javascript, all numbers are encoded as double precision floating point numbers, following the international IEEE 754 standard. This format stores numbers in 64 bits, where the number, the fraction (AKA mantissa), is stored in bits 0 to 51, the exponent in bits 52 to 62, and the sign in bit 63.

Floating-point numbers are represented as binary (base 2) fractions. Regrettably, most decimal fractions cannot be represented exactly as binary fractions. The decimal floating-point numbers you enter are only approximated by the binary floating-point numbers actually stored in the machine. That being said, you'll see that floating-point arithmetic is NOT 100% accurate.

```
0.2 + 0.1
0.30000000000000004
0.3 - 0.1
0.19999999999999998
1111.11+1111.11+1111.11+1111.11+1111.11
5555.549999999999
```

Even more, you can lose precision when performing operations, such as addition and subtraction, on decimal numbers with very different absolute value.

```
99999999999.0123 + 0.00231432423
99999999999.01462
```

There are more examples. Integer numbers between -(2^{53} -1) and 2^{53}+1 are accurately represented. Beyond this threshold, not all integer numbers can be represented. You can think that in Javascript, integer numbers are accurate up to 15 digits.

```
Math.pow(2, 53) - 1
9,007,199,254,740,991 //Max positive safe integer in Javascript
Number(999999999999999);
999999999999999
Number(9999999999999999);
10000000000000000
```

In this post, I’ll present two different strategies to overcome these precision issues: one for decimal numbers and another for integer numbers.

## Strategy for decimal numbers

There are two things to tackle here, representation and handling of decimal numbers.

In order to prevent losing precision, decimal values must be serialized as strings and not as JSON numbers. But also, there should be a way to tell if a JSON string value corresponds to a decimal number or is merely a string. One solution could be to create a custom representation for decimal numbers, for instance:

```
amount: { _type: "BigDecimal",
_value_str: "1234.56"}
```

Having covered representation part, we need an alternative to Javascript numbers. There are several libraries, such as *big.js*, *bignumber.js*, *decimal.js* and *bigdecimal.js*, to address this issue. All of them provide arbitrary-precision decimal arithmetic (find benchmarks here). If you don’t need to perform complex arithmetic operations, such as logarithms, square roots, etc., and mostly, you do additions, subtractions, multiplications, and divisions; then *big.js* library could be the best choice.

```
//Creating a Big object (Throws NaN on an invalid value)
var amount1 = new Big(“0.1”); //
var amount2 = Big(“0.2”); // 'new' is optional
amount1.plus(amount2); // the result is a new Big object with value 0.3
```

The last piece of the solution would be an HTTP interceptor to convert decimal numbers from their custom JSON representation to *big.js *(or to the implementation you chose) objects and vice versa.

## Strategy for integer numbers

As mentioned before, the safe range for JSON integers is -2^{53} < x < 2^{53}. So, let's analyze what can fit inside that range:

- 2
^{53}milliseconds has us covered for around ±300,000 years. Do you need to handle dates outside this range? - Assuming that you represent DB table ids as integers. Are you going to insert in a table more than 9 quadrillion rows?
- In addition to DB tables ids, in case you use integers to represent sort orders, quantities, amount of days, etc. Do you need to support integer quantities over 9,007,199,254,740,991?

In most scenarios, we could declare that there is no problem. Our code breaks when provided with obscenely large numbers, but we simply do not use numbers that large and we never will.

So, as a solution for integer values, we'll reject values outside of the safe range, even when they fit in a double. For this, you can use custom serializers/deserializers to prevent sending/receiving integer values outside of the safe range.

## Summary

It's very important to tackle the Javascript numeric precision issue sooner than later. Ideally, before you start writing the first line of code of your application. Once you realize that this issue affected your application, maybe it's too late and some data has been corrupted.

## About the Author

Sebastian is a passionate software engineer. He has been working in BPM projects for the last 10 years mostly focused on user interface development and user experience enhancement. He participated in projects in several countries around America, such as Argentina, Brasil, Chile, Bolivia, Uruguay, Panama and USA.

Sebastian loves playing tennis and soccer. He is a big Boca Juniors fan and never misses a game at 'La Bombonera'.

## Join the Conversation