
Working with Numbers
All numbers in JavaScript are 64-bit floating-point values. Unlike other languages, there is no internal differentiation between floating-point values and integers. JavaScript provides several objects containing functions that are orientated specifically around floating-point values and integers. However, these objects are semantic. Therefore, applying integer-specific functions to a number will still result in a floating-point value.
Numbers are the simplest form of data represented by the JavaScript engine. Number values are immutable, which means their value cannot be modified in memory. If you assign a new number to a variable, you are simply overwriting the old value with the new. The existing value is not modified.
Since numbers are passed to variables by value, it is not possible for two variables to point to the same number address space. Therefore, working with number values is considered pure, providing you do not reassign values to variables.
Arithmetic Limitations
Floating-point values can pose something of a problem in JavaScript. Due to their binary encoding, which is how the number is represented in bits within the JavaScript engine, simply adding two floats together may not produce the result you are expecting. Consider the following:
0.1 + 0.2; // outputs 0.30000000000000004
Here, the response should be 0.3, but it is not. The underlying runtime simply does not handle values in a way that allows them to be accurate, even with a single decimal place.
If accuracy is necessary for your application, there are a number of tricks that provide the correct output. With regards to the preceding example, simply converting the values into decimals before the addition will yield better accuracy. You can then convert the resulting value back into a floating-point number afterward, like so:
((0.1 * 10) + (0.2 * 10)) / 10; // outputs 0.3
The same is also true for multiplication and division:
0.0032 * 13; // outputs 0.041600000000000005
However, if you convert into an integer first, then the result is more accurate:
0.0032 * 1000 * 13 / 1000; // outputs 0.0416
This limitation is not restricted to JavaScript. In fact, any language that works with 64-bit IEEE 754 floating-point numbers will have the same limitations. There are numerous libraries available on the internet that help with these issues, if you would prefer not to tackle them yourself.
Note
The largest integer values that JavaScript can represent as numbers are 9,007,199,254,740,991 and -9,007,199,254,740,991.
The Number Object
As we mentioned previously, numbers in JavaScript are primitives. As such, they have no properties or methods. Contrary to this, however, the JavaScript engine maintains an awareness of where numerical literals and variables are used within your application and provides syntactic support for methods via the number object. It is even possible to extend this object using prototypes, which will be explained in full in Part Four. Any extension imposed on the Number object will be usable against numeric values in your application:
5.123.toPrecision(3);
// returns "5.12"
Note that while it may seem as though numerical values are objects, this is not actually the case. In memory, numbers are very simple values. The Number object, and its implementation by the JavaScript runtime, merely provides many of the benefits afforded with objects against these values.
Number Functions
The Number object contains an assortment of functions that work with numeric values. Like all objects, the Number object provides a constructor that, if invoked with the new keyword, creates a Number object instance. Numbers that are created with the Number constructor are actual objects, which is contrary to the previous statement, that is, that numbers are not objects, and is the cause of a lot of confusion. To make things even more interesting, the resulting object instance can be treated just like any other number.
In addition to the constructor is the Number function. This is used in the same manner as the Number constructor but without the new keyword. Invoking this function returns a number, not an object:
var num1 = 99;
var num2 = Number(99);
var num3 = new Number(99);
console.log(num1 == num2); // outputs 'true'
console.log(num1 == num3); // outputs 'true'
console.log(num2 == num3); // outputs 'true'
console.log(num1, num2, num3); // outputs '99 99 Number {99}'
In all the instances detailed in the preceding code, the resulting values can be worked with in the same manner and with the same rules, except when dealing with truthy conditionals. Typically, conditionals see the value 0 (zero) as a falsey value, but the value returned from new Number(0) is truthy, even though it is also zero.
console.log( false==new Number(0) ); // => true, meaning that Number(0) equals to false, but:
if( new Number(0) ) { // => truthy
console.log("truthy");
}
else {
console.log("falsey");
}
Likewise, when comparing by type, the value that's returned from new Number(0) is an object, not a number, so strict comparisons against numeric literals will fail.
Both the Number function and constructor will accept any value type. If the value type cannot be converted into a number, then NaN (not a number) is returned:
console.log( Number(true) ); // 1
console.log( Number(false) ); // 0
console.log( Number("5") ); // 5
console.log( Number([]) ); // 0
console.log( Number([1, 2, 3]) ); // NaN
When working with JavaScript, it is advised not to use the Number constructor at all so that your code is more readable.
Aside from the Number function and constructor, the global Number object also provides a variety of functions to help us identify or parse numeric values:

Figure 5.11: Number functions and their descriptions
Each of these functions is static and so must be preceded with the global Number object (which acts as a class in many languages), except when using parseFloat or parseInt. These functions are also global and therefore can be invoked without the preceding Number, like so:
console.log( Number.parseFloat("1.235e+2") ); // outputs 123.5
console.log( parseFloat("1.235e+2") ); // outputs 123.5 again
Number Methods
Since the JavaScript parser semantically identifies numeric values, it is possible to invoke instance methods of the Number object against them, just like we can with actual objects. The majority of these methods are used to format numeric values as string representations, which is very useful for presentation in web pages:

Figure 5.12: Number methods and their descriptions
Using a combination of the Number functions and methods, it is possible to convert to and from numeric values as necessary, though some precision may be lost:
console.log( 123.456.toLocaleString() ); // outputs "123.456"
console.log( 123.456.toFixed(1) ); // outputs "123.5"
console.log( 123.456.toExponential(3) ); // outputs "1.235e+2"
However, calling those functions on integer literals (rather than floats) fails:
console.log( 123.toString() ); // => Uncaught SyntaxError: Invalid or unexpected token
When JavaScript sees the first dot right after one or more digits, it assumes you want to write a float literal. There are some workarounds to this:
console.log( 123.0.toString() ); // Append .0. It will still be represented as an integer (as far as it fits in the integer range)
console.log( (123).toExponential(2) ); // Wrap within parentheses (..)
Number Properties
The global Number object provides a variety of constant properties, which is useful when comparing your numeric values. The most important of these is NaN. Being able to identify numeric discrepancies outside of the JavaScript runtime's ability to calculate provides you with a means to reduce bugs in your code. For instance, observe the following example:
var num = 999 / 0;
When executed, the result of num is the constant value known as Infinity. Since it is not possible to add, deduct, multiply, or divide other values from infinity, any further math against that value will also be Infinity. Therefore, being able to deduce this restriction within your code will provide an early warning that something may be amiss in your logic.
Other properties of Number include the following:

Figure 5.13: Number properties and their descriptions
Both MAX_SAFE_INTEGER and MIN_SAFE_INTEGER are interesting values. Consider the following code:
Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2;
Surprisingly, the result of the preceding expression is true. This is simply because the numbers exceed safety boundaries and are therefore no longer accurately represented. The precision that's used in both sides of the preceding expression results in the same value and thus are considered equal.
Exercise 5.05: Currency Formatter
In this exercise, we will create a function that can take a numeric value parameter as a number or string and format it into a price value with two-decimal precision. In order to prepend a currency symbol, the function will accept it as a parameter. Let's get started:
- Define your function signature. This function will accept two parameters. The first of these will be the decimal value and the second will be the currency symbol:
function formatPrice(value, currency) {
- When executing, the first task that the function performs should be to validate the quality of the passed parameter values. The value parameter must be able to be converted into a numeric value, while the currency parameter should be a character string. If the currency is falsey, such as when no parameter has been passed, then we can default its value to the dollar symbol:
value = Number(value);
currency = currency || "$";
- When responding to errors, there are many ways we can notify the caller that something went wrong. In this instance, we'll simply return null. This way, the caller will know that anything other than a string response means that something wasn't quite right:
if (Number.isNaN(value) || typeof currency != "string") {
return null;
}
- Now that we know the parameters are usable, combine them into the correct format and return the value:
return currency + value.toFixed(2);
}
- If you go ahead and execute this function, you will see the appropriate responses:
console.log( formatPrice(1.99, 32) ); // => null
console.log( formatPrice(5, "£") ); // => £5.00
console.log( formatPrice(9.9) ); // => $9.90
console.log( formatPrice("Ted") ); // => null

Figure 5.14: Exercise 5.05 output
We can see the output once all four functions are run in the preceding figure. In this exercise, we created a function that took a numeric value parameter as a number or string and formatted it into a price value with two-decimal precision.