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