Number, Math

  1. Written forms
  2. Zero division, Infinity
  3. NaN
  4. Conversion to a number
  5. Permissive conversion: parseInt and parseFloat
  6. Imprecise calculations
  7. Rounding
    1. Rounding to given precision
    2. toFixed(precision)
  8. Random numbers
  9. Summary

All numbers in JavaScript, both integer or floating-point are of type Number.

Internally a number is represented by the floating-point format IEEE 754, also called “double precision”. There are 8 bytes per number.

The maximal integer value is about 253.

Written forms

In JavaScript, it is possible to write numbers using hexadimal or octal radix:

alert( 0xFF ) // 255 in hexadimal form, starts with 0x
alert( 010 ) // 8 in octal form, starts with 0

A so-called scientific form is also available, it consists of a number followed by “e” and quantity of zeroes.

For example, 1e3 is 1 with 3 zeroes, effectively 1000.

// scientific form: 3 with 5 zeros
alert( 3e5 )  // 300000

If the quantity of zeroes is negative, then the number is shifted past the decimal point.

// shift 5 times past the decimal point.
alert( 3e-5 ) // 0.00003

Zero division, Infinity

Imagine, you are to create a new language. People will call it “JavaScript” (or LiveScript.. whatever).

What should happen if someone divides by zero?

Usually the answer is “Zero Division Error”. At least, for most programming languages it is like that.

But JavaScript creators decided to go more mathematical way. In maths, when you divide by 0, you get Infinity (or -Infinity).
Same in JavaScript:

alert(1/0)  // Infinity
alert(-1/0) // -Intinify

Infinity is a special numeric value in JavaScript and behaves just like it should. Infinity is larger than any other number. Adding anything to Infinity doesn’t change it:

alert(Infinity > 999999999999999999999999999);
alert(Infinity + 5 == Infinity);

So there is no error, just infinity.

NaN

If a mathematical operation can’t be performed, it returns a special pseudo-numerical value: NaN (Not-A-Number).

For example, division of zero by zero is not defined in mathematical sense, so it returns NaN.

alert( 0 / 0 )  // NaN

NaN has following properties:

  • NaN is not equal to anything, including itself.

    The code below is silent:

    if (NaN == NaN) alert("== works"); // Neither of these
    if (NaN === NaN) alert("=== works"); // will work
    

  • NaN can be checked only by isNaN - a special function which returns true for NaN and false for any other value.
    var n = 0/0
    
    alert( isNaN(n) ) // true
    
  • NaN is sticky. Any math operation with NaN gives NaN.
    alert( NaN + 1 ) // NaN
    

Mathematical operations can’t lead to an error or crash in JavaScript.

At worst, NaN is returned.

Conversion to a number

The strict conversion can be done by “+”

var s = "12.34" 
alert( +s )  // 12.34

The string is parsed and if its format is numeric, then the number is returned.

Actually, all mathematical functions excepts binary plus '+' convert a string to number:

var s = "12.34" 
alert( -"12.34" / "2" )  // -6.17

Parsing into a number ignores whitespaces at start and end. For example:

alert( +"  12")  // 12
alert( +" \n34  \n") // 34, newlines are whitespace symbols too

If the value can’t be converted to a number, the operation returns NaN:

alert( +"12test" )  // NaN

isNaN converts it’s argument into a number automatically. So it can be used to check whether a string represents a number:

var x = "-11.5"
if (isNaN(x)) {
  alert("Not a number")    
} else {
  alert("Number")    // isNaN(x) = false means it's a number
}

But please, be careful, because a string with whitespaces is converted to 0:

alert(isNaN(" \n\r\t  ")) // false, "..spaces.." is 0

And, of course isNaN won’t work for other types, because false, null, undefined are also converted to 0.

Actually, the most reliable conversion check is either a regexp or isNumeric below:

function isNumeric(n) {
  return !isNaN(parseFloat(n)) && isFinite(n)
}

isNumeric correctly checks numericality for all input types.

Make an interface to prompt for two numbers and then alert their sum.
It should work like this page.

Note. There’s a pitfall ahead. Watch the types.

Open solution
Solution

Check source for the solution: tutorial/intro/sum.html.

Permissive conversion: parseInt and parseFloat

In real-life, many values are not exactly numbers. Especially, those “10pt” or “-12px” used in CSS.

The “+” operator can’t convert them into a number, because it checks the strict format. It will return NaN:

alert( +"12px" ) // NaN

That’s where parseInt jumps in:

var v = parseInt('12px')
alert(v)

parseInt and its friend parseFloat convert the value character by character until they meet something impossible to convert. Then it stops and returns what could be converted.

alert( parseFloat('12.3.4') ) // 12.3, 1st dot is fine, but not the 2nd

A minor pitfall with parseInt on some browsers is that ECMAScript specification allows it to guess the radix.

The older version of the specification treats a string starting with 0 in parseInt as octal:

alert( parseInt('0xFF') ) // 255
alert( parseInt('010') )  // in some browsers 8, because 0 means octal

If you want be sure that “010” means 10, use the second optional argument to pass the radix:

alert( parseInt('010', 10) )

Note, parseInt/parseFloat returns NaN if conversion stops at first char:

alert( parseInt('a123') )

Imprecise calculations

The floating point format leads to loss of precision. Minor computational errors may occur.

Please, run the following:

alert(0.1 + 0.2 == 0.3)

Did you run it? If not, please do.

Ok, you did. So what’s up? Maybe the browser is buggy? Change the browser, run it again.

Ok, well, now you can be sure: 0.1 + 0.2 is not 0.3. Then what is it?

alert(0.1 + 0.2)

Now you see, there is a minor calculation error.

That’s because internal floating-point format represents a number in binary form. But, just like 1/3 can’t be represented in decimal form (it’s 0.3333…),
0.1(=1/10) cannot be exactly represented as binary, and 0.2(=2/10) as well.

Their binary representations are cut at some point. Here you are:

alert( 0.1.toFixed(20) )  // 0.10000000000000000555

When you sum two inaccuracies, you get the minor calculation error.

Of course, that doesn’t mean you can’t sum numbers in JavaScript. In fact, you can.

Actually, there are two ways to sum 0.1 and 0.2:

  1. Make them integers, sum and then divide back:
    alert( (0.1*10 + 0.2*10) / 10 ) // 0.3
    

    It works, because 1 and 2 can be represented in binary form exactly. So, the sum is exact.

  2. Sum and then round to fixed precision as described in the next section. Rounding to 10-th decimal digit will chop off the calculation error.
    alert( +(0.1+0.2).toFixed(10) )
    

Another funny example

Look, I’m a self-increasing number!

alert(9999999999999999)

The reason is, of course, precision loss. The number format cannot store that many digits exactly.

The following loop hangs up the browser. Why?

var i = 0
while(i != 10) { 
  i += 0.2
}

Open solution
Solution

That’s because i never equals 10.

Run the following to see real values of i:

var i = 0
while(i < 11) { 
  i += 0.2
  if (i>9.8 && i<10.2) alert(i)
}

Note that neither value equals 10.

Rounding

One of most often operations with numbers is rounding. In JavaScript, there are 3 functions for basic rounding.

Math.floor
Rounds down
Math.ceil
Rounds up
Math.round
Rounds to nearest

alert( Math.floor(3.1) )  // 3
alert( Math.ceil(3.1) )   // 4
alert( Math.round(3.1) )  // 3

Note how floor and ceil work for negative numbers:

alert( Math.floor(-3.1) )  // -4, rounds to nearest less than -3.1
alert( Math.ceil(-3.1) )   // -3, rounds to nearest greater than -3.1

Ultra-fast rounding with bitwise operators

Bitwise operators cut off the decimal part automatically when applied.

In the example below, 12.3 is rounded by XOR’ing with 0:

alert( 12.3^0 )  // 12

The XOR ^0 was chosen, because it doesn’t change the number. Any bitwise operator, which doesn’t modify the number, will do. For example, double NOT ~~12.3, right shift to 0 bits 12.3>>0 etc.

Note that for negative numbers cutting of the decimal part is not the same as Math.floor.

Rounding to given precision

It is often required round to precision, like: two digits after the decimal point. An old trick is multiply and divide on 10 with given number of zeroes:

var n = 3.456
alert( Math.round( n * 100 ) / 100 )  // 3.456 -> 345.6 -> 346 -> 3.46

toFixed(precision)

There is also a method toFixed(precision) can be called directly on a number.

It rounds the number to given precision and returns a string:

var a = 12.34
alert( a.toFixed(1) ) // "12.3"

The returned string is right-padded with zeroes if needed:

var a = 12.34
alert( a.toFixed(5) ) // "12.34000"

So, if we need a number, we can convert it back by adding “+” to n.toFixed():

var a = 12.34
alert( +a.toFixed(5) ) // 12.34

Random numbers

The Math.random() returns a random number from 0(inclusive) to 1(exclusive):

alert( Math.random() )

Most often recipes regarding random numbers are represented as a set of tasks. Check them out below.

Write a code to return a random float value between 0(inclusive) and max(exclusive).

Open solution
Solution

What we need is to generate a value between 0..1 and multiply it by max:

var max = 10

alert( Math.random()*max )

Write a code to return a random float value between min(inclusive) and max(exclusive).

Open solution
Solution

The classical way to solve it is to generate a random value in range 0..max-min, then shift it by min.

var min=5, max = 10

alert( min + Math.random()*(max-min) )

Write the code to generate a random integer value between min and max, with both min,max as possible values.

Any value from min..max range should happen with the same probability.

Open solution
Solution

The wrong way

A first idea could be to generate a value between min..max and round it.

But the probability of edge values min and max will be two times less than any other value.

For example, let’s find random between 1 and 3. We take the following code as the source:

// random float value from min(inclusive) to max(exclusive)
var rand = min + Math.random()*(max-min) 

// so for 1 and 3
var rand = 1 + Math.random()*(3-1)

The Math.round() will map the resulting rand like this:

1   ... 1.499+ will map to 1
1.5 ... 2.499+ will map to 2 
2.5 ... 2.999+ will map to 3

Note that the first range (for 1) is of length 0.5, the second (for 2) has length 1, and the third range is again 0.5.

So, 2 will happen two times more often than 1 or 3, the probability will not be same as required.

The good way

A better way is to Math.floor() a number from min to max+1.

So, to find a random integer between 1 and 3, we need to generate a random float between 1(inclusive) to 4(exclusive) first.

Then the Math.floor() will map them like this:

1 ... 1.999+ will map to 1
2 ... 2.999+ will map to 2
3 ... 3.999+ will map to 3

You can see, the ranges are same, so the probability is uniform.

The resulting code is:

var min=5, max=10
*!*
var rand = min + Math.random()*(max+1-min) 
rand = Math.floor(rand)
*/!*
alert(rand)

Another nice solution

The following also works. Please figure out why.

var rand = Math.round(random(min-0.5, max+0.5))

Summary

In this section you’ve learned about numbers in JavaScript:

  • Which written forms exist.
  • An imprecision which is a consequence of the internal number format.
  • How errors are handled: Infinity and NaN.
  • How to round numbers.
  • How to convert a string to number in a permissive way, for values like “12px”.

There are more methods and mathematical actions upon numbers. Consult the manual about Number and Math objects.

Tutorial

Donate

Donate to this project