## 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
–2^{53} to 2^{53}. 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.

## 4.1. Keeping an Incremental Counter

### 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

### 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

### 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

### 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

### 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.

<!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:

Create each table row cell using

`document.createElement("td")`

.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).Append the text node to the table cell.

Append the table cell to the table row.

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.

<!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.

### 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.