© J R Stockton, ≥ 2012-08-19

# JavaScript Maths.

## General

Page JavaScript Demos contains a demonstration of code to evaluate an expression, to find a zero of an expression, to find a peak of an expression, and to approximate by continued and rational fractions (cf. π = 355/113).

The evaluator is not limited in fact to a single expression, and seems rather useful for testing odd bits of JavaScript. A simpler copy is now in JavaScript/HTML/VBS Quick Trials; it can also do HTML and VBS, and pack text, and indent code. Also :

## Data Types

There are three basic data types: "Boolean", "Number", "String". Values of the corresponding types are returned by Boolean(x), Number(x), String(x) for any x.

There are compound data types :- the type "Object" includes "Array" and "RegExp" as subtypes.

An Object contains something like a table of name-value pairs.

There are six permissible String return values of the operator typeof :- "boolean", "function", "number", "object", "string", "undefined".

Note that input controls return only Strings, even if the input looks like a number; and that the binary operator + is a string concatenator if at least one of its two operands is a string ...

### String Type

Strings are made of 16-bit Unicode characters. There is no practical upper limit on their length; empty strings are not special.

Literal strings are bounded by matching " or ' characters, and so can contain the other character. The escape character is \ which accordingly is represented by \\; other particular cases are \" \' \n \t \x## \u#### for " ' newline, tab, hexadecimal ##, and Unicode hexadecimal #### - \x## = \u00##.

Within code, Strings should generally not be used to represent numbers or Booleans. String input can be converted on input with unary + or !! operators respectively, as below.

### Boolean Type

When a variable is used to store one of two states, the Boolean values true / false should generally be used. It is inefficient to store, for example, Strings "yes" / "no" if those forms are not used as such for I/O.

When a Boolean is required, only the following values are considered to mean false :-
0 (numeric zero), "" (empty string), false, NaN, null, undefined.

The unary NOT operator ! gives a Boolean result from an argument of any type.

The following operators all give a Boolean result :-

   !     <  <=  >  >=     ==  !=  ===  !==      &&   ||


Boolean() converts its argument to Boolean, as does the dual-unary operator !!. The following should be equivalent :-

DocProp = (document.Prop?true:false)
DocProp = Boolean(document.Prop)
DocProp = document.Prop != null
DocProp = !!document.Prop


Number(BoolExp) and +BoolExp return 0 if the expression is false and 1 if it is true.

The following should not be used :- X==true X!=false X==false X!=true - instead, use respectively :- X X !X !X.

### Number Type - integer / floating-point

JavaScript currently only has one number type, floating-point, which is encapsulated as the object type "Number". The actual value is held in binary as an IEEE 754 / IEC 559 Double. The range therefore slightly exceeds ±1.7×10308.

There are special values :-

• The single value NaN - Not-A-Number - is unequal to everything, including itself; there is a function isNaN (IEEE Doubles can hold many different NaN values, but JavaScript does not discriminate between them).
• There are two non-finite numbers - plus and minus Infinity; there is a function isFinite.
• Plus and minus zero are representable and equal, but are distinguishable by comparing their reciprocals with zero.
• Denormals, where the exponent is lowest and the mantissa starts with 0, have lessened proportional resolution; the smallest are about ±4.94×10-324.

Integers can only be held accurately up to 53 bits plus sign, and numbers are only, at best, accurate to 15-16 significant decimal digits. Any integer from 0 up to 253 = 9,007,199,254,740,992 in magnitude is stored exactly, but relatively few decimal fractions can be. Only the numbers obtainable from one of those integers by multiplying by 2N-53 for any N in -1023 to +1023 can be held exactly. Check in detail, though.

For conversion of ordinary values of Number to/from bit strings, and the exact value of Numbers, see in Exact Arithmetic.

A general understanding of the properties and use of floating-point numbers is necessary for almost any arithmetic beyond counting; see Pascal Floating-Point for links to general information on floating-point numbers and arithmetic, and Pascal / Delphi / + Types for data on type Double. IEEE Doubles are handled directly by the FPU of a PC; I don't know the situation on other processors.

Seek the edited reprint (large: 500K + graphics) of a paper What Every Computer Scientist Should Know About Floating-Point Arithmetic, by David Goldberg, (1991); while parts are esoteric, much is easy to read. One URL.

#### Intrinsic Arithmetic Errors

Given that Numbers are Doubles, arithmetic is as exact as possible, but no more. Simple binary fractions are exact, provided no more than 53 bits in all are needed. Operations on integers are exact if the true result and all intermediates are integers within that range, except where bitwise operators are used, when the range is 32-bit.

Note that pound-pence arithmetic, using values such as £12.34, may have unexpected small errors; I find that 3355.53 + 660.97 - 660.97 evaluates to 3355.5299999999997. Do the arithmetic in pence - 335553 + 66097 - 66097 gives 335553 - or be sure to round to pence sufficiently often. 1142.33 + 44545.66 gives 45687.990000000005.

And 1.26+11.67+3.45+2.97 evaluates to 19.349999999999998, not 19.35. Indeed, 0.05+0.01 gives 0.060000000000000005 and 0.06+0.01 gives 0.06999999999999999. Also, -0.07+0.05+0.02 gives -3.469446951953614e-18; this illustrates that code to output non-negative numbers as strings may need to accommodate slightly negative ones.

And 3*0.1 gives 0.30000000000000004, and 1.1*1.1 evaluates to 1.2100000000000001.

Note that, while A+B = B+A, it may be that A+B+C != C+B+A, since that is a comparison of (A+B)+C and (C+B)+A which may round differently; for example, X = [0.03+0.03+0.01, 0.01+0.03+0.03] gives me [0.06999999999999999, 0.07].

Where it is known that a calculated number should have an integer value, one can use Math.round() directly to remove its rounding errors.

In particular, non-integer computed results should not normally be compared for equality; and generally need rounding for display.

For example, X*0.01 often differs slightly from X/100 - the latter is better, since 100 is stored exactly and 0.01 cannot be. Example : X = 35.

##### Math.trunc / Math.ceil

There must be a danger with Math.trunc() / Math.ceil(), in cases where the argument should ideally be an exact integer, that rounding errors may have placed it on the "wrong side" of the exact value, after which trunc / ceil will give a result differing by one from what it should ideally be.

## Operators

The unary + - operators.

### Bitwise Operators

The following bitwise operators - ~ << >> >>> & ^ | - work on 32-bit integers; conversion to/from Number is automatic. They are, respectively, complement, shift left, shift right arithmetic, shift right, and, xor, or.

They repay consideration; they seem fast. See in Uses of Operators.

### Mod & Div

In any case where it may matter, it is important to check the behaviour of any "float-to-integer" type of operation for negative arguments; and any rounding for "half-way" arguments.

Mod on negative numbers has the usual difference from what I generally need; it gives :-
(-33 % 10) → -3   rather than   → +7 .
A workround is to add a sufficient multiple of the second argument to the first.

However, Math.floor is as I would wish :-
Math.floor(-2.5) → -3   and not   → -2.

Thus the following code gives the Mod that I prefer, an alternative, and a more expressive Div. Note : Mod & mod give different results for negative Y and can give slightly different results for non-integer Y.

For numbers in the signed 32-bit range, X|0 truncates X towards zero; it seems quicker than Math.floor(X).

For the fractional part of X, use X % 1.0 or Mod(X, 1.0).

## Assignments

Assigning a value which is a simple number, string, or boolean creates a new copy; changing the copy does not change the original. This is "Pass by Value".

Assigning an Object entity creates a new copy of the pointer to that entity; the original Object is not duplicated. Likewise for Arrays, which are in fact Objects themselves; and String Objects. This is "Pass by Reference".

## Function Parameters

These appear to behave as for Assignments. Parameters are passed by value, and the value of an object for this purpose is effectively a pointer to it.

So the external values of simple parameters cannot be changed; but the properties of objects can be.

"Objects, including Arrays, are passed by reference. Simple variables are always passed by value."

## Conversion Functions

A string of digits starting with a zero is sometimes considered to be in octal - only, I think, for numeric literals, and when parseInt() has no second parameter, in which case octal seems probable but deprecated. So parseInt("09") cannot be trusted. A string starting 0x is by default taken as hexadecimal.

## Trigonometry

Results and arguments which represent angles are in radians; one circle equals 2π radians equals 360 degrees equals 400 grads equals 6400 mils. Don't enter a numeric value for π, etc.; use Math.PI, etc., assigning its value to a variable if brevity is needed. Note that canvas routines use radians, but SVG routines use degrees.

The arguments of Math.atan2() are, in JavaScript as in other languages, (y,x) in that order; some documentation errs here. Don't try to build atan2() from Math.atan(); it's pointless and error-prone. Use Math.atan2() in preference to Math.atan() in Cartesian work.

## Some Routines

### Big Factorials

Since the JavaScript Number type is an IEEE Double, it can hold factorials only up to 170! (and exactly only up to about 18!). Strings approximating to larger values can be constructed.

Stirling's approximation, to sufficient terms, can also be used to calculate the logarithm of the factorial.

### Base Converters for Integers

Note that, in some circumstances, a value 231 ≤ N < 232 may be shown in Hex as a number in 0..0x7fffffff preceded by a minus sign; and that this may be browser-dependent.

#### Using Standard Coding

Bases 2..36

Result

That uses parsefloat(X, base).toString(radix); the input values are NOT validated. Even where that approach is unsuitable, there can be no need to use Math.pow(). Try it with the output base not 10 and the output value large!

Currently (June 2010). .toString(radix) is not cross-browser reliable with non-integer Numbers; the final character can for example be the radix, or zero.

Results

Bases 2..

### Bit Counter

The operation N = N & N-1 decrements the number of bits set in N; somewhere, there is a Web page listing many such operations.

## Decimal and Thousands Separators

The Decimal Separator (decimal point), where needed, is vital; but thousands separators are mere decoration.

Consider multi-national number formats.

The only decimal separator known to JavaScript (future versions possibly excepted) is the decimal point, "."; and the language knows no thousands separator. If the Continental virgule (a comma) is needed in input or output, it seems to me best to deal with it by using a RegExp substitution as a pre- or post- processor. Input thousands separators can be removed with a RegExp; I have code below and elsewhere to add them in output.

Data which is stored or transmitted should not use a localised format; the format should be fixed and where possible compliant with standards of ISO and similar bodies.

### Output Test

If you see a comma (',') reported as the decimal separator, please tell me : I do not expect it to occur.

### Decimals : Exchanging Separators

JavaScript expects, and gives, a dot as the decimal separator, but Continental usage calls for a comma :-

S = S.replace(/,/, '.') // for input
S = S.replace(/\./, ',') // for output


For integers with thousands separators, use the above in the opposite order and with a RegExp g modifier.

For strings with both separators possible, to exchange dot and comma throughout,

S = S.replace(/,/g, '#').replace(/\./g, ',').replace(/#/g, '.')


### Trailing Point-Zero for Integers

It may be required that a Number is converted to String, with integers having trailing ".0". Function TRZ converts integers, without harming or rounding other Numbers.

### Insertion of Thousands Separators

Consider multi-national number formats. Before a thousands separator, India prefers separators two digits apart, to show lakhs, crores, etc. The following can be changed to insert dot rather than comma.

Note that ThouS is for non-negative integers, and Comma is for integers.

To insert commas as thousands separators :-

#### Recursive Thousands Separators

Unicode "\u20AC" is the Euro sign, "€" or "€"; "\u00A3" is the Pound sign, "£". Function RComma() can process more than one number in a string. A "g" does not affect the result; and, in MSIE 4, it did not significantly affect the speed. See Regular Expressions.

### Removal of Thousands Separators

Commas can be removed from a string for input :-

S = S.replace(/,/g, '')
S = S.split(',').join()


## Numeric Input

### Numeric String to Number

The .value properties of JavaScript text-input controls are of type String.

After possible pre-processing, the quantity represented by an input string may need to be used as a Number. If so, usually it should immediately (but after any pattern validation; see RegExps & Validation) be converted to Number type.

One can use, to convert String S to Number, any of : +S -S Number(S) parseInt(S) parseInt(S,B) parseFloat(S), or any other operator requiring an arithmetic operand. Different converters have differing properties.

Unlike the unary operators, parseFloat/parseInt disregard trailing non-numerics. All of those conversions disregard whitespace; all disregard leading zeroes, except sometimes for parseInt(S).

#### Unary + -

The unary + operator converts its argument to type "number". For converting String to Number, it seems preferable to parseInt / parseFloat except when their special properties are required. It is shorter and faster.

Unary + is a good way to fix the '1' + '1' = '11' problem (FAQ, 6.4). In reading a control, V1 = + document.forms['AForm'].Min1.value makes V1 a number; without the + it still would be a string.

While typeof(0+'') gives 'string', typeof(+'') gives 'number'.

The unary - operator is similar; so - - (not --) is in effect a numeric + operator.

They take numbers as decimal unless starting with 0x (or 0X), meaning hexadecimal; a leading zero does not mean octal. Leading whitespace is ignored; leading or trailing non-numerics give NaN. An empty string gives zero.

#### Number()

Number() can be expected to behave as unary +.

#### Deprecated

S+0 S-0 S*1 S/1 are deprecated, as calling for an unnecessary operation; use +S.

#### Function parseInt()

Remember that if parseInt is given only one argument, and that starts with zero, then the argument is generally taken as octal (unless starting with 0x, when as hexadecimal); giving two arguments is safer. Except for leading whitespace and sign, the first non-numeric character terminates the number.

Remember that a literal integer is interpreted as hexadecimal if the numeric part starts with 0x.

Function parseInt(S, 10) can be used to read the integer part of a decimal fixed-point number, independently of which decimal separator is used, if any.

When a zero is returned by parseInt / parseFloat, it is correctly signed.

##### After RobG & self, news:c.l.j 2006-08

For converting a possibly signed base-B digit string S to a Number, function parseInt should be used only when beneficial, as it is longer and slower than alternatives.

For values of numeric properties, given in decimal without a leading zero and possibly followed by a unit (such as when getting the value of a style property, e.g. 33px), parseInt(S) is appropriate.

Bases 2 to 7, 9, 11 to 15, 17 to 36 always require parseInt(S, B).

In bases 8, 10 and 16, a string S of non-negative integer value with non-numeric parts removed (e.g. 09kg has been trimmed to 09), can be of the forms :-

        S        B      Conversion              Note
0123     8      use parseInt(S, 8)      1
0123    10      unary + preferred       2
2345    10      unary + preferred       2
0x6b4   16      unary + preferred
6b4     16      use parseInt(S, 16)

Notes :

1: B is needed to work with all browsers and with ECMA-262 3rd Edn
2: Commonly use parseInt when converting the value of form controls to Number


#### Function parseFloat()

Note : +'0x1' differs from parseFloat('0x1') and +'' differs from parseFloat('').

ECMA-262's global function parseFloat is decimal-only, handling such as 3.142 864e5.

A function such as parseFloat(S, R) can be written in JavaScript, with or without the use of ECMA-262's global functions, with two different ends in mind :-

• to provide a reasonably fast and accurate function to fill the gap left by ECMA, in which case its behaviour with malformed inputs should match that of ECMA parseFloat(S),
• to provide a function of guaranteed accuracy for testing, in which case it need only handle well-formed inputs.

For approximate and exact routines, see in Exact Arithmetic.

## Numeric Output

For rounding to a given number of decimal places or significant figures, see my Rounding 0.

Only a string can have a leading zero; these functions always return a string. Some of these, and others, are tested and timed at String and Number Formatting.

To bring integers in 0-9 to two digits, or 0-99 to three digits, by adding leading zeroes, without mistreating out-of-range numbers :-

#### String Input

This may sometimes be better; it also accepts Number parameters, and Hex strings (==1 or <2 ?) :-

#### Replacement

This prefixes single digits within word boundaries throughout :-

For more, use SpcsTo() / ZeroTo / ChrsTo, or build on one of PadNumber#() which assume Num>=0.

This has moved into Some Bugs in JavaScript Implementations.

### Number to English Words

#### Non-Negative Integer to English Words

Script version ≥ #1

Use at your own risk; it is not tested enough for use with real currency or on cheques.

Is there interest in the reverse operation?

#### Others

For a Number X which may be negative, use Math.abs(X) after dealing with the sign.

For non-integers, obtain the integer and fractional parts of the Number X with |0 %0 and treat separately.

## Programming for Precision

Arithmetic on non-integers is usually inexact, but sometimes, with cate and understanding, it can be made strictly exact.

### Using Integers

Money calculations using integer-euros-point-cents will be unreliable. It is generally better to work in integer cents; it may be useful to work in submultiples (such as Delphi's "currency", a 64-bit integer "comp" scaled by 10000).

### Avoiding Subtraction

Subtraction of similar non-integers leads to increased relative errors. For example, the algebraic formula for the roots of a quadratic includes a ± sign. Rather than using both directly, it will be better to obtain the first root by whichever sign gives an augmentation and the second by then using the formula for the ratio of the roots.

### Avoiding Reversals

Where possible, remove factors common to the numerator and denominator of an overall expression, as in B = π * A ; ... ; C = B / π ; and similarly with addition and subtraction.

### Exactness of Constants

It is better to multiply or divide by an integer than to divide or multiply by its reciprocal. To see that there can be a difference, evaluate 35/100 - 35*0.01 for example.

### Logarithms

Getting a base-10 lograrithm by dividing a base-e logarithm by a constant is inexact. Try Math.log(1000)/Math.log(10) for example.

### General

Converting from exact algebra to optimum executable code is an art which should be attempted only with care and consideration.

## Errors in IE

Consolidated into Some Bugs in JavaScript Implementations.

MathJax
JavaScript to typeset Maths in Web pages
My MathJax General Demo Page