== Why is this an issue?

Numbers in JavaScript are stored as double-precision 64-bit binary format IEEE 754. Like any other number encoding occupying a finite number of bits, it is unable to represent all numbers.

The values are stored using 64 bits in the following form:

* 1 bit for the sign (positive or negative)
* 11 bits for the exponent (2^n^). -1022 &le; n &le; 1023
* 52 bits for the significand (or mantissa)

// When images can be added, uncomment the block
//image::IEEE_754_Double_Floating_Point_Format.svg.png[]

The actual value of the stored number will be `(-1)^sign^ * (1 + significand) * 2 ^exponent^`

Given this structure, there are limits in both *magnitude* and *precision*.

Due to the 52 bits used for the significand, any arithmetic in need of more precision than 2^-52^ (provided by `Number.EPSILON`) is subject to rounding.

In terms of magnitude, the largest number the 64 bits of the format can store is 2^1024^ - 1 (`Number.MAX_VALUE`).

However, because the 52 bits of the significand, only integers between -(2^53^ - 1) (`Number.MIN_SAFE_INTEGER`) and 2^53^ - 1 (`Number.MAX_SAFE_INTEGER`) can be represented exactly and be properly compared.

== How to fix it

JavaScript provides the helper function `Number.isSafeInteger()` to test if a number is between the safe limits.

When you need to store a large number, use `BigInt`. `BigInt` and `Number` can be compared between them as usual, but pay attention that operations (`+` `pass:[*]` `-` `%` `pass:[**]`) between both types raise an error unless they are coerced to the same type, so you will have to adapt your code accordingly.

When in need of more decimal precision, it is recommended to use a dedicated library to ensure that calculation errors are not introduced by rounding.

This code snippet shows some cases where the value stored will be rounded.

[source,javascript]
----
const foo = 2312123211345545367  // Noncompliant: will be stored as 2312123211345545000
const bar = BigInt(2312123211345545367);  // Noncompliant: parameter is first parsed as an integer and thus rounded
const baz = 0.123456789123456789 // Noncompliant: will be stored as 0.12345678912345678
----

Instead, these could be used:

[source,javascript]
----
const foo = BigInt('2312123211345545367');
// OR
const bar = 2312123211345545367n;

// use a library like decimal.js for storing numbers containing many decimal digits
import {Decimal} from 'decimal.js';
const bar = new Decimal('0.123456789123456789');
----

//=== Pitfalls

//=== Going the extra mile

== Resources

=== Documentation

* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding[MDN Number encoding]
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt[MDN BigInt]
* https://en.wikipedia.org/wiki/Double-precision_floating-point_format[Wikipedia Double-precision floating-point format]
* https://en.wikipedia.org/wiki/IEEE_754[Wikipedia IEEE 754 Standard]
//=== Articles & blog posts
//=== Conference presentations
//=== Standards
