69 lines
3.0 KiB
Plaintext
Raw Normal View History

== Why is this an issue?
2023-06-06 16:55:51 +02:00
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.
2023-06-01 14:51:27 +02:00
The values are stored using 64 bits in the following form:
2023-06-01 14:51:27 +02:00
* 1 bit for the sign (positive or negative)
2023-06-06 16:55:51 +02:00
* 11 bits for the exponent (2^n^). -1022 ≤ n ≤ 1023
* 52 bits for the significand (or mantissa)
2023-06-06 16:55:51 +02:00
// When images can be added, uncomment the block
//image::IEEE_754_Double_Floating_Point_Format.svg.png[]
2023-06-06 16:55:51 +02:00
The actual value of the stored number will be `(-1)^sign^ * (1 + significand) * 2 ^exponent^`
2023-06-06 16:55:51 +02:00
Given this structure, there are limits in both *magnitude* and *precision*.
2023-06-01 14:51:27 +02:00
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.
2023-06-01 14:51:27 +02:00
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.
2023-06-06 16:55:51 +02:00
== How to fix it
2023-06-01 14:51:27 +02:00
2023-06-06 16:55:51 +02:00
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.
2023-06-01 14:51:27 +02:00
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]
----
2023-06-01 14:51:27 +02:00
const foo = 2312123211345545367 // Noncompliant: will be stored as 2312123211345545000
2023-06-06 16:55:51 +02:00
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
----
2023-06-01 14:51:27 +02:00
Instead, these could be used:
[source,javascript]
----
2023-06-06 16:55:51 +02:00
const foo = BigInt('2312123211345545367');
2023-06-01 14:51:27 +02:00
// OR
2023-06-06 16:55:51 +02:00
const bar = 2312123211345545367n;
2023-06-01 14:51:27 +02:00
2023-06-06 16:55:51 +02:00
// use a library like decimal.js for storing numbers containing many decimal digits
2023-06-01 14:51:27 +02:00
import {Decimal} from 'decimal.js';
const bar = new Decimal('0.123456789123456789');
----
//=== Pitfalls
//=== Going the extra mile
== Resources
=== Documentation
2023-06-01 14:51:27 +02:00
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number#number_encoding[MDN Number encoding]
2023-06-06 16:55:51 +02:00
* 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