Number

The number primitive type is used to express numerical data. It stores this data in the double-precision 64-bit floating-point format (IEEE 754). 64 bits here refers to there being 64 binary digits available to store information. The entire 64-bit format that's used in the IEEE 754 standards can be broken down into three chunks:

  • 1 bit for the sign of the number being represented: Whether the number is positive or negative
  • 11 bits for the exponent of the number: This tells us where the radix or decimal dot resides
  • 52 bits for what's termed the fraction or significand: This tells us the integer value
A side effect of this floating-point formation means that there are technically two zeros: positive zero (+0) and negative zero (-0). Thankfully, in JavaScript, you don't have to be explicit when checking for these values. Both will return true when compared with the strict equality operator ( +0 === -0) and both are considered falsy.

Technically, there are 53 bits available (not 52) for the expression of an integer value as the leading bit of the significand field resides within the exponent field. This is an important clarification as it has a direct effect on how much precision we can get from JavaScript numbers. Having 53 bits available to express an integer value means that any numbers greater than 253-1 are considered unsafe. These safety limits are available as constants on the Number object:

  • Integers larger than 253 or 9007199254740991 (Number.MAX_SAFE_INTEGER)
  • Integers smaller than -253 or -9007199254740991 (Number.MIN_SAFE_INTEGER)

The loss of precision beyond these bounds can be observed if we try to perform addition on the upper limit:

const max = Number.MAX_SAFE_INTEGER;
max + 1; // => 9007199254740992 (correct)
max + 2; // => 9007199254740992 (incorrect)
max + 3; // => 9007199254740994 (correct)
max + 4; // => 9007199254740996 (incorrect)
// ... etc.

Here, we can see that the evaluated additions are incorrect. Beyond MAX_SAFE_INTEGER, all mathematical operations will be similarly imprecise.

It is still possible to express values larger than MAX_SAFE_INTEGER within JavaScript. Many values up to 21024 (Number.MAX_VALUE) can be expressed, but many cannot. Therefore, it is considered very unwise to attempt to express numbers beyond Number.MAX_SAFE_INTEGER.

To sum this up, any values between Number.MIN_SAFE_INTEGER and Number.MAX_SAFE_INTEGER are safe to use and will provide integer precision, while values beyond these bounds should be considered unsafe. If we feel ourselves needing an integer outside of these bounds, then we can use JavaScript's BigInt primitive:

const max = BigInt(Number.MAX_SAFE_INTEGER)
max + 1n; // => 9007199254740992n (correct)
max + 2n; // => 9007199254740993n (correct)
max + 3n; // => 9007199254740994n (correct)
max + 4n; // => 9007199254740995n (correct)
// ... etc.

We'll explore the BigInt primitive further in a later part of this section. For now, just remember to always consider the largeness of your numbers and whether they can be fully accommodated by JavaScript's Number type. It's also important to consider the precision of decimal values (such as in fractions) as well. When expressing decimals in JavaScript, you'll likely encounter issues like this:

0.1 + 0.2; // => 0.30000000000000004

This is due to inherent mechanism by which fractions are expressed in the floating-point standard. You can imagine that if we were interested in querying whether a decimal is equal to, greater than, or less than another value, it would be as simple as using the following code:

const someValue = 0.1 + 0.2;
if (someValue === 0.3) {
yay();
}

But yay() will never run. To get around this problem, there are two options. The first involves something called the epsilon. The epsilon is the margin of error inherent to floating-point math, and JavaScript makes this available to use as Number.EPSILON:

Number.EPSILON; // => 0.0000000000000002220446049250313

This is a very tiny number, but it must be taken into account if we are to have a hope of doing basic mathematical operations on decimals. If we wish to compare two numbers, we can simply subtract them from each other and check that the margin is less than the EPSILON:

const someValue = 0.1 + 0.2;
if (Math.abs(someValue - 0.3) < Number.EPSILON) {
// someValue is (effectively) equal to 0.3
}

The other approach we can take is to convert any decimals we're dealing with into integers expressed by either Number or BigInt types. So, if we have a need to represent values from 0 to 1 with a precision of eight decimal places, for example, then we can simply multiply these values by 100,000,000 (or 108):

const unwieldyDecimalValue = 0.12345678;

// We can use 1e8 to express Math.pow(10, 8)
unwieldyDecimalValue * 1e8; // => 12345678

Now, we are free to conduct integer math on these values and divide them back down into their fractions when done. It's crucial to note that any decimal value longer than 15 digits cannot be expressed in JavaScript's Number type, so you'll need to explore other options. JavaScript currently doesn't have a native BigDecimal type, but there are many third-party libraries available that fulfill a similar purpose (you can easily find these online).

If you ever find yourself needing to operate on large or very precise numbers in JavaScript, or if your code concerns sensitive matters such as finance, medicine, or science, it's absolutely crucial to take the time to fully understand what levels of precision you require and whether JavaScript can natively support those needs.

There's one more topic to discuss under the Number type, and that is NaNNaN is a primitive that technically belongs to the Number type. It represents a failure to parse something as a number; for example, Number('wow') evaluates to NaN. Since typeof NaN is a number, we should check for a valid number in the following way:

if (typeof myNumber === 'number' && !isNaN(myNumber)) {
// Do something with your number
}

The value NaN can create a headache when its existence is not foreseen. It'll usually crop up in areas where you're attempting to cast strings to numbers or where this happens implicitly (coercion).

We'll be covering the topic of coercion, casting, and detection more in the next chapter. This will include a section where we get into the complexity of NaN and compare isNaN(), the global function, to the slightly different Number.isNaN(). For now, it's only important to appreciate that NaN is its own distinct value and is itself, oddly, considered a number within JavaScript.

There is another value encapsulated by the Number type that is not a normal number: Infinity. You will receive Infinity when you attempt to do mathematical operations such as dividing by 0:

100/0; // => Infinity

Infinity, like NaN, is a globally available primitive value that you can reference and check for:

100/0 === Infinity; // => true

There is also -Infinity, which is technically a distinct value:

100/-0; // => -Infinity
-Infinity === Infinity; // => false

Infinity, like NaN, is of the Number type, so when passed to the typeof operator, it will evaluate to "number":

typeof Infinity; // => "number"

Outside of Infinity, -Infinity, and NaN, all values that are of the Number type can be considered regular everyday numbers. Overall, and for most use cases, the Number type is very simple to use and operate on. It is, however, vital to know about its limitations, many of which we've covered here so that you can make an informed decision about when it may not be appropriate to use.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.178.237