logo To Foot
© J R Stockton, ≥ 2010-02-21

JavaScript Maths.

No-Frame * Framed Index * Frame This
Links within this site :-

See "About This JavaScript Site" in JavaScript Index and Introduction.

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

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 a hash table of name-value pairs.

There are six permissible String return values of the operator typeof :- "boolean", "function", "number", "object", "string", "undefined". I've seen it suggested that MS IE7 / JScript also can use "unknown" and "date".

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 \\; some particular cases are \n \t \x## \u#### for newline, tab, hexadecimal ##, and Unicode hexadecimal ####.

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

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

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.

2001-04-06 : Jim wrote : "ECMAScript Ed 4. will almost certainly have Integer types etc. and will probably have an optional Decimal arithmetic package, ...". However, Edition 4 is abandoned, and the mid-2009 draft of Edition 5 regrettably shows no sign of either.

General Decimal Arithmetic - page set, with links, from IBM Hursley.

Seek also 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,

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 signed 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 & (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 signed 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).

See also Pascal Maths.

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 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. A string starting 0x is taken as hexadecimal.

Trigonometry

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

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.

See also Pascal Maths.

Some Routines

Sorting

Moved to Sorting and Order.

Big Factorials

Since the JavaScript Number type is an IEEE Double, it can hold factorials only up to 170! (and exactly only up to 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

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

That uses parseInt(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!

Using General Coding







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 (new releases 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.

Crores and Lakhs

See Indian numbering system.

 
 
 

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, 4.21). In reading a control, V1 = + document.forms['AForm'].Min1.value makes V1 a number; otherwise 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.

See also The unary + operator by Brad Fults.

Number()

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

Function parseInt()

Remember that if parseInt is given only one parameter, and that starts with zero, then the parameter is taken as octal (unless starting with 0x, when as hexadecimal); and that the first non-numeric character terminates the number.

Remember that a literal integer is interpreted as hexadecimal if it 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.

MISPLACED ?
parseInt (trailing characters accepted; base can be supplied, otherwise if starting with 0x is Hexadecimal else if with 0 is Octal else Decimal)

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()

Function parseFloat is decimal-only, handling such as 3.142 864e5.

N.B. +'0x1' differs from parseFloat('0x1') and +'' differs from parseFloat('').

Deprecated

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

Errors in IE

toFixed

See in my Rounding 1.

Number from String

Reported 2004-07-17 GMT in "JScript v5.6.8513, Windows Script Host v5.6."; then seen by me in MSIE4 "JScript 3.1.2124" and in MSIE6 "JScript 5.6.8831", presented more simply :-

A fairly long fixed-point input number String, but not a fairly long integer, is misconverted to Number in MSIE 4 to 8, but not in Firefox 2/3, Opera 9, Safari 3/4, or Chrome 3.

The error also appears in parseFloat() and Number().

Numeric Output

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

Simple Leading Zeroes

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

Padding

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

Number to English Words

Non-Negative Integer to English Words

Script version ≥ #1

   

Also input your own.

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 almost always inexact.

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.

Home Page
Mail: no HTML
© Dr J R Stockton, near London, UK.
All Rights Reserved.
These pages are tested mainly with Firefox 3.0 and W3's Tidy.
This site, http://www.merlyn.demon.co.uk/, is maintained by me.
Head.