Chapter 4. Working with Numbers and Math

4.0. Introduction

Numbers and numeric operations in JavaScript are managed by two different JavaScript objects: Number and Math.

Like the String and RegExp objects discussed in earlier chapters, numbers can be both a literal value and an object. No surprises there, but the Math object is different: it has no constructor, and all properties and methods are accessed directly from the object.

The Number Object and Number Literal

Numbers in JavaScript are floating point, though there may not be a decimal component present. If no decimal is present, they act as if they’re integers:

var someValue = 10; // treated as integer 10, in base 10

Numbers can be defined in the range of –253 to 253. Most numbers in JavaScript are literal values, assigned as values to variables, and used in various computations:

var myNum = 3.18;
var newNum = myNum * someValue;

You can also construct a Number using a constructor method:

var newNum = new Number(23);

You can assign a literal number to a variable, but when you access a Number method on the variable, a Number object is created to wrap the literal value, which is discarded when the method is finished.

The Number object’s methods provide various display operations, such as providing an exponential notation:

var tst = .0004532;
alert(tst.toExponential()); // outputs 4.532e-4

In addition, there are several static Number properties, which can only be accessed via the Number object directly:

alert(Number.MAX_VALUE); // outputs 1.7976931348623157e+308

There’s a special Number static property, NaN, which is equivalent to the global NaN, and stands for Not a Number. Anytime you try to use a value in a number operation that can’t be parsed as a number, you’ll get a NaN error:

alert(parseInt("3.5")); // outputs 3
alert(parseInt("three point five")); // outputs NaN

The Math Object

Unlike the Number object, the Math object does not have a constructor. All of the object’s functionality, its properties and methods, are static. If you try to instantiate a Math object:

var newMath = new Math();

You’ll get an error. Rather than create a new Math instance, access properties and methods directly on the object instead:

var topValue = Math.max(firstValue, secondValue); // returns larger number

The Math object has a considerable number of properties and methods, including several trigonometric methods. The precision of the methods is at the same level of precision that we would find using a language like C.

Table 4-1 provides a listing of the Math properties, and Table 4-2 contains a listing of the Math methods.

Table 4-1. Math object static properties

Property

Purpose

E

The number value for e, the base of natural logarithms

LN2

Natural logarithm of 2

LN10

Natural logarithm of 10

LOG2E

Base 2 logarithm of e, and the reciprocal of LN2

LOG10E

Base 10 logarithm of e, and the reciprocal of LN10

PI

The number for π

SQRT1_2

Square root of 1/2, reciprocal of SQRT2

SQRT2

Square root of 2

Table 4-2. Math object static methods

Method

Purpose

abs (x)

Returns absolute value of x; if x is NaN, returns NaN

acos (x)

Returns arc cosine of x; if x is greater than 1 or less than 0, returns NaN

asin (x)

Returns arc sine of x; if x is greater than 1 or less than –1, returns NaN

atan (x)

Returns the arc tangent of x

atan2 (x, y)

Returns the arc tangent of the quotient of x, y

ceil (x)

Returns the smallest integer equal to or greater than x

cos (x)

Returns the cosine of x

exp (x)

Returns Ex where E is the base of natural logarithms

floor (x)

Returns the largest integer equal to or less than x

log (x)

Returns logarithm of x

max (x1, x2, ..., xn)

Returns largest of given arguments

min (x1, x2, ..., xn)

Returns smallest of given arguments

pow (x,y)

Returns result of raising x to power of y

random ()

Returns random number greater than or equal to 0, and less than 1

round (x)

Rounds number to closest integer

sin (x)

Returns the sine of x

sqrt (x)

Returns the square root of x

4.1. Keeping an Incremental Counter

Problem

You want to maintain an incremental counter in code.

Solution

Define a number variable, either locally or globally, or as part of an object’s properties, and increment the variable’s value with each iteration of code:

var globalCounter = 0;
function nextTest() {
   globalCounter++;
   ...
}

Discussion

The simplest way to increase or decrease a number is using the increment (++) and decrement (--) operators, respectively. They’re equivalent to:

numValue = numValue + 1; // equivalent to numValue++
numValue = numValue - 1; // equivalent to numValue--

Both operators can be used prefix or postfix, which means the operators can be placed before or after the operand. How they’re positioned is significant. If the operator is placed before the operand, the operand’s value is adjusted first, before the operand is used:

var numValue = 1;
var numValue2 = ++numValue; // numValue and numValue2 are both 2

If the operator is postfix (placed after the operand), the operand is used first, and then its value is adjusted:

var numValue = 1;
var numValue2 = numValue++; // numValue is 2 and numValue2 is 1

The point at which the counter is incremented depends on its use. If it’s needed in a loop, the value is incremented in the loop:

var counter = 0;
while (counter <= 10) {
   ...
   counter++;
}

If the counter is needed more globally, it can be declared as a global variable, but use with caution. A global variable is one that’s declared outside of a function, and isn’t redeclared within a function. It can easily conflict with any other global variables that might exist in the application or other libraries you use:

var counter = 0;
function someFunction() {
   counter++;
}

Another approach is to add the counter as property to an object, persisting as long as the object, and accessible by all object methods.

See Also

Chapter 16 covers how to create JavaScript objects.

4.2. Converting a Decimal to a Hexadecimal Value

Problem

You have a decimal value, and need to find its hexadecimal equivalent.

Solution

Use the Number object’s toString method:

var num = 255;
alert(num.toString(16)); // displays ff, which is hexadecimal equivalent for 255

Discussion

By default, numbers in JavaScript are base 10, or decimal. However, they can also be created and used in hexadecimal and octal notation. Hexadecimal numbers begin with 0x (a zero followed by lowercase x), and octal numbers always begin with zero:

var octoNumber = 0255; // equivalent to 173 decimal
var hexaNumber = 0xad; // equivalent to 173 decimal

Other base numbers can be created using the Number object’s toString method, passing in the base radix, in a range from 2 to 36:

var decNum = 55;
var octNum = decNum.toString(8); // value of 67 octal
var hexNum = decNum.toString(16); // value of 37 hexadecimal
var binNum = decNum.toString(2); // value of 110111 binary

To complete the octal and hexadecimal presentation, you’ll need to concatenate the zero to the octal, and the 0x to the hexadecimal value.

Although decimals can be converted to any base number (between a range of 2 to 36), only the octal, hexadecimal, and decimal numbers can be manipulated, directly, as numbers.

See Also

The decimal to hexadecimal conversion is used in Recipe 4.4.

4.3. Creating a Random Number Generator

Problem

You need to generate a random number, between 0 and 255.

Solution

Use a combination of JavaScript Math methods: random to generate a random value between 0 and 1, which is then multiplied by 255, and floor to truncate the number.

var randomNumber = Math.floor(Math.random() * 255);

Discussion

The random method generates a random number between 0 and 1. To increase the range, multiply the result by the upper end of the range of values you want. If you need a random number with a higher lower end, such as a number between 5 and 10, multiply the value from random by a number equal to the upper range, minus the lower range, minus 1, and then add the lower range to the result:

var randomNumber = Math.floor(Math.random() * 6) + 5;

The floor method rounds down the floating-point value to the nearest integer.

4.4. Randomly Generating Colors

Problem

You need to randomly generate a web color.

Solution

Use the Math object to randomly generate each RGB (Red-Green-Blue) value:

function randomVal(val) {
  return Math.floor(Math.random() * val);
}

function randomColor() {
   return "rgb(" + randomVal(255) + "," + randomVal(255) + "," +
randomVal(255) + ")";
}

Discussion

Web color can be expressed either in hexadecimal notation, or as an RGB value. With the RGB value, each color is represented as a number between 0 and 255. The example demonstrates one technique to generate a color, using one function to randomly generate the number, and a second to return an RGB formatted string.

Older browsers may not support the RGB notation. To use a hexadecimal notation, the randomColor function can be altered to:

function randomColor() {
   // get red
   var r = randomVal(255).toString(16);
   if (r.length < 2) r= "0" + r;

   // get green
   var g = randomVal(255).toString(16);
   if (g.length < 2) g= "0" + g;

   // get blue
   var b = randomVal(255).toString(16);
   if (b.length < 2) b= "0" + b;

   return "#" + r + g + b;
}

The hexadecimal notation is used (#ffffff), and the generated decimal number is converted to hexadecimal notation, using the Number object’s toString method. Since a decimal value of something like zero converts to a single-digit character and the format needs to be double-digit, the length is tested and modified accordingly.

All the target browsers support both the RGB and hexadecimal notation, except IE7, which only supports hexadecimal.

See Also

See Recipe 4.1 about converting between decimal and hexadecimal notation, and Recipe 4.3 for how to randomly generate numbers.

4.5. Converting Strings in a Table to Numbers

Problem

You want to access values in an HTML table and convert to numbers for processing.

Solution

Access the numbers using the Document Object Model (DOM) API, and use the global function parseInt to convert the strings to number values:

 var rows = document.getElementById("table1").children[0].rows;
var numArray = new Array();

for (var i = 0; i < rows.length; i++) {
   numArray[numArray.length] = parseInt(rows[i].cells[1].firstChild.data);
}

Discussion

The parseInt global function has two arguments: a required numeric string, and an optional radix (base). If the radix is not provided, it’s assumed to be 10, for decimal.

If the string provided doesn’t contain a number, NaN is returned. If the string contains a partial number, the parser will convert the number up to the point where a nonnumeric value is reached, and return the result:

var numString = "133 hectares";
var numHectares = parseInt(numString); // returns 133

If the number is in floating-point format, parseInt stops when it reaches the decimal, and returns just the integer part of the number. If the string could contain a floating-point number and you want the result to be a floating-point number, use the parseFloat global function, instead:

var numString = "1.458 hectares";
var fltNum = parseFloat(numString); // returns 1.458

4.6. Summing All Numbers in a Table Column

Problem

You want to traverse all of the values in a table column, convert the values to numbers, and then sum the values.

Solution

Traverse the table column containing numeric values, convert to numbers, and sum the numbers:

var sum = 0;

 // use querySelector to find all second table cells
 var cells = document.querySelectorAll("td:nth-of-type(2)");

 for (var i = 0; i < cells.length; i++)
      sum+=parseFloat(cells[i].firstChild.data);

Discussion

Both global functions parseInt and parseFloat convert strings to numbers, but parseFloat is more adaptable when it comes to handling numbers in an HTML table. Unless you’re absolutely certain all of the numbers will be integers, parseFloat can work with both integers and floating-point numbers.

As you traverse the HTML table and convert the table entries to numbers, sum the results. Once you have the sum, you can use it in a database update, print it to the page, or pop up a message box, as the solution demonstrates.

You can also add a sum row to the HTML table. Example 4-1 demonstrates how to convert and sum up numeric values in an HTML table, and then how to insert a table row with this sum, at the end. The code uses document.querySelectorAll, which uses a CSS selector, td + td. This selector finds all table cells that are preceded by another table cell.

Example 4-1. Converting table values to numbers and summing the results
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Accessing numbers in table</title>

<script type="text/javascript">
//<![CDATA[

window.onload=function() {

   var sum = 0;

   var dataTable = document.getElementById("table1");

   // use querySelector to find all second table cells
   var cells = document.querySelectorAll("td + td");

   for (var i = 0; i < cells.length; i++)
      sum+=parseFloat(cells[i].firstChild.data);

   // now add sum to end of table
   var newRow = document.createElement("tr");

   // first cell
   var firstCell = document.createElement("td");
   var firstCellText = document.createTextNode("Sum:");
   firstCell.appendChild(firstCellText);
   newRow.appendChild(firstCell);

   // second cell with sum
   var secondCell = document.createElement("td");
   var secondCellText = document.createTextNode(sum);
   secondCell.appendChild(secondCellText);
   newRow.appendChild(secondCell);

   // add row to table
   dataTable.appendChild(newRow);

}

//--><!]]>
</script>
</head>
<body>
<table id="table1">
   <tr>
      <td>Washington</td><td>145</td>
   </tr>
   <tr>
      <td>Oregon</td><td>233</td>
   </tr>
   <tr>
      <td>Missouri</td><td>833</td>
   </tr>
</table>
</body>
</html>

Being able to provide a sum or other operation on table data is helpful if you’re working with dynamic updates via an Ajax operation, such as accessing rows of data from a database. The Ajax operation may not be able to provide summary data, or you may not want to provide summary data until a web page reader chooses to do so. The users may want to manipulate the table results, and then push a button to perform the summing operation.

Table rows are simple to add, as long as you remember the steps:

  1. Create a new table row using document.createElement("tr").

  2. Create each table row cell using document.createElement("td").

  3. Create each table row cell’s data using document.createTextNode(), passing in the text of the node (including numbers, which are automatically converted to a string).

  4. Append the text node to the table cell.

  5. Append the table cell to the table row.

  6. Append the table row to the table. Rinse, repeat.

If you perform this operation frequently, you’ll most likely want to create functions for these operations, and package them into JavaScript libraries that you can reuse. Also, many of the available JavaScript libraries can do much of this work for you.

See Also

See more on JavaScript libraries in Chapter 17. View more demonstrations of creating web page components in Chapter 12. The document.querySelectorAll is one of the new Selectors API methods, and won’t work with older browsers. It is not supported in IE7. For new browsers, there may also be restrictions on its use. More examples and discussion of the Selectors API can be found in Recipe 11.4.

4.7. Converting Between Degrees and Radians

Problem

You have an angle in degrees. To use the value in the Math object’s trigonometric functions, you need to convert the degrees to radians.

Solution

To convert degrees to radians, multiply the value by (Math.PI / 180):

var radians = degrees * (Math.PI / 180);

To convert radians to degrees, multiply the value by (180 / Math.PI):

var degrees = radians * (180 / Math.PI);

Discussion

All Math trigonometric methods (sin, cos, tin, asin, acos, atan, and atan2), take values in radians, and return radians as a result. Yet it’s not unusual for people to provide values in degrees rather than radians, as degrees are the more familiar unit of measure. The functionality provided in the solution provides the conversion between the two units.

4.8. Find the Radius and Center of a Circle to Fit Within a Page Element

Problem

Given the width and height of a page element, you need to find the radius of the largest circle that fits within that page element, and its center point.

Solution

Find the smaller of the width and height; divide this by 2 to find the radius:

var circleRadius = Math.min(elementWidth, elementHeight) / 2;

Given the page element’s width and height, find the center by dividing both by 2:

var x = elementWidth / 2;
var y = elementHeight / 2;

Discussion

Working with graphics requires us to do things such as finding the center of an element, or finding the radius of the largest circle that will fit into a rectangle (or largest rectangle that can fit in a circle).

Example 4-2 demonstrates both of the solution calculations, modifying an SVG circle contained within an XHTML document so that the circle fits within the div element that surrounds it.

Example 4-2. Fitting a SVG circle into a div element
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Using Math method to fit a circle</title>
<style type="text/css">
#elem
{
   width: 400px;
   height: 200px;
   border: 1px solid #000;
}
</style>
<script type="text/javascript">
//<![CDATA[

function compStyle(elemId,property) {
   var elem = document.getElementById(elemId);
   var style;
   if (window.getComputedStyle)
     style=window.getComputedStyle(elem,null).getPropertyValue(property);
   else if (elem.currentStyle)
      style=elem.currentStyle[property];
   return style;
}
window.onload=function() {
  var height = parseInt(compStyle("elem","height"));
  var width = parseInt(compStyle("elem","width"));

  var x = width / 2;
  var y = height / 2;

  var circleRadius = Math.min(width,height) / 2;

  var circ = document.getElementById("circ");
  circ.setAttribute("r",circleRadius);
  circ.setAttribute("cx",x);
  circ.setAttribute("cy",y);
}
//--><!]]>
</script>

</head>
<body>
<div id="elem">
   <svg xmlns="http://www.w3.org/2000/svg" width="600" height="600">
      <circle id="circ" width="10" height="10" r="10" fill="red" />
   </svg>

</div>
</body>

Figure 4-1 shows the page once it’s loaded. There are techniques in SVG that can accomplish the same procedure using the SVG element’s viewPort setting, but even with these, at some point in time you’ll need to polish off your basic geometry skills if you want to work with graphics. However, as the example demonstrates, most of the math you’ll need is relatively simple, and basic.

Page with SVG circle fit into rectangular div element
Figure 4-1. Page with SVG circle fit into rectangular div element

See Also

Finding a circle’s radius and the center point of an element is important when working with both SVG and Canvas, covered in Chapter 15. The method used to find the computed width and height of the div element can be found in Recipe 13.2. Recipe 12.15 covers the setAttribute method.

SVG is not supported in IE8 and earlier, but should be supported in IE9.

4.9. Calculating the Length of a Circular Arc

Problem

Given the radius of a circle, and the angle of an arc in degrees, find the length of the arc.

Solution

Use Math.PI to convert degrees to radians, and use the result in a formula to find the length of the arc:

// angle of arc is 120 degrees, radius of circle is 2
var radians = degrees * (Math.PI / 180);
var arclength = radians * radius; // value is 4.18879020478...

Discussion

The length of a circular arc is found by multiplying the circle’s radius times the angle of the arc, in radians.

If the angle is given in degrees, you’ll need to convert the degree to radians first, before multiplying the angle by the radius.

See Also

The Math trigonometric methods provide essential functionality for creating various Canvas and SVG effects, discussed in Chapter 15. Recipe 4.7 covers how to convert between degrees and radians.

Get JavaScript Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.