70 lines
3.0 KiB
Plaintext
Raw Normal View History

// If you want to factorize the description uncomment the following line and create the file.
//include::../description.adoc[]
== Why is this an issue?
2023-06-01 14:51:27 +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, as trying to represent an infinite amount of numbers with a finite amount of memory is not possible.
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)
* 11 bits for the exponent (2^n^). Range is from -1022 to 1023
* 52 bits for the significand
2023-06-01 14:51:27 +02:00
The actual value of the stored number will be:
2023-06-01 14:51:27 +02:00
`Number = (-1)^sign^ * (1 + significand) * 2 ^exponent^`
2023-06-01 14:51:27 +02:00
Given this structure, there are some 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.
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]
----
2023-06-01 14:51:27 +02:00
const foo = 2312123211345545367 // Noncompliant: will be stored as 2312123211345545000
const bar = 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-01 14:51:27 +02:00
const foo = BigInt(2312123211345545367);
// OR
const foo = 2312123211345545367n;
import {Decimal} from 'decimal.js';
const bar = new Decimal('0.123456789123456789');
// use a library like decimal.js for storing numbers containing decimal digits
----
//=== 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]
* 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]
2023-06-01 14:51:27 +02:00
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt[MDN BigInt]
//=== Articles & blog posts
//=== Conference presentations
//=== Standards