logo To Foot
© J R Stockton, ≥ 2009-09-26

JavaScript Rounding 0 : Introduction.

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

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

General Rounding

See also page Pascal/Delphi Rounding and Truncation, for further general discussion of rounding; also Wikipedia and Rounding Algorithms 101 - Maxfield and Brown.

Number to Integer

Generally, Round(X) rounds to the nearest integer, Floor(X) / Trunc(X) rounds down, and Ceil(X) rounds up. The cases of X being negative, half-way between integers, or both, always need careful consideration; different languages, and maybe different implementations of languages, can do it differently.

Number to Fraction

To round a Number to a Fraction, commonly to a power of 0.1, multiply by the inverse of the power, round to integer, and divide by the power. It is better to use factors which are represented exactly : integers, rather than their reciprocals.

Number to String

To get a given number of decimal places, the result must be a String, so that trailing zeroes can be indicated.

Rounding a Number to a String with a given number of decimal places, or in other formats, is often required. Many languages provide appropriate routines; JavaScript default is more limited, but code can be provided.

Bankers' Rounding

Bankers' Rounding requires that the exact half-way case be rounded to an even least digit.

Statistical

In statistical rounding, a fractional part of value X is rounded up a fraction X of the time, and otherwise down.

Rounding in JavaScript

JavaScript Numbers

JavaScript stores the value of a Number as an IEEE Double, in binary floating-point. Currently it has no other formats. There are two zero values, +0 and -0; the sign usually has no effect, but it is detectable.

Integers smaller than about ±9E15 are held exactly. So are positive/negative-power-of-two multiples of such integers, within range limit about ±1.7E308. So are sums of such multiples, provided that the span of powers-of-two does not exceed about 53. See in JavaScript Maths.

Most values, including most of those with one, two, or three decimal places, are not stored exactly.

Bit Representations of Number shows conversion to/from binary and the exact values of Numbers.

It is essential that the intrinsic properties of the Number type be understood.

Precision

JavaScript storage of and arithmetic on non-integers is necessarily inexact (almost always); see in JavaScript Maths and Pascal Floating-Point. For example, displaying 0.05+0.01 gives 0.060000000000000005 and 0.06+0.01 gives 0.06999999999999999. Such numbers should be rounded to a suitable format for presentation.

Most real numbers, in particular those with finite base-10 representations, cannot be held exactly in a Double. This is why, for instance, Math.round(1.035*100) gives 103, but Math.round(2.035*100) gives 204 and Math.round(1.045*100) gives 105; moreover, RoundToNdp(1.0035, 3) gives 1.004, but RoundToNdp(1.035, 2) gives 1.03.

One can write 1.3550, or "1.3550", in code, or read the string "1.3550" from a control, and then store it in a variable V as type Number, and then convert it to a String "1.355"; but that does not mean that V actually has a value exactly equal to 1.355 (which cannot be represented exactly). Rounding to two decimal places by arithmetic will, quite correctly, be controlled by the sign of the difference from 1.355. Alternatively, one might convert the value of V to a String, which act itself includes some rounding; and then round that to two places by operations on the individual characters.

If doing arithmetic other than addition, subtraction, and multiplication by an integer, one should consider what sort of intermediate rounding is indeed proper for the field; rounding to pence internally is not necessarily legitimate.

When a calculated number is to be converted to a string with, say, two decimal places, it must be rounded arithmetically; it cannot just be extended or truncated if nominally a multiple of 0.1 or 1.0; this is because of imperfect accuracy in calculation.

JavaScript Primitives

The JavaScript primitives taking a general Number X and returning an adjacent integer value are :-

All can give +0 -0.

Math.ceil(-X) = -Math.floor(X)

All systems have a method returning String :-

Later systems have more methods returning String, for which see in Rounding 1 :-

Bankers' Rounding, for half-way going to the nearest even number, is not provided.

Method Math.round

ISO/IEC 16262 15.8.2.15 specifies that Math.round of a half-integer must choose the value closer to plus infinity (a dubious expression, since all finite values are infinitely far from plus infinity; it should be "rounds towards ...").

A Number that displays like 234.5p is held exactly. It is half-way between two integers and will Math.round towards plus infinity (or away from zero in a routine that separates the sign). Bankers' Rounding is meaningful, but requires additional code.

A Number that displays like £2.345 is only held approximately. It is therefore not half-way between pence and should Math.round to the nearest penny. Bankers' Rounding to pence is only meaningful for the odd eighths of a pound - an odd number of half-crowns, in fact. Therefore, Bankers' Rounding of a pounds Number to the nearest penny is not meaningful, except in 4% of cases. See also #.##5 to #.## - up or down?.

To get exact results, make the smaller unit an integer - use pence rather than pounds. Half-pence are held exactly.

Types of Rounding

Nature of Result

Rounding a Number to a Number which is a multiple of 10-N, and Formatting a Number to a Decimal String with exactly N decimal places (implying possible trailing zeroes), are two rather different things. In the latter case, there may also be a need for Extending to (at least) a fixed number of digits/spaces before the decimal point, for visual alignment. In technical work, it may be better to use Rounding to N Significant Figures for output.

Change of Value

One should always consider when to round up and when to round down. The default rounding in MSIE 4 JavaScript was different from that in Delphi 3; therefore, if there is a relevant standard, at least one of them did not comply with it.

In particular, when dividing odd numbers by two, or doing anything equivalent, one should determine what sort of rounding to integer is correct; see Bankers' Rounding.

Specific rounding was legally required in converting between the old Euro-land national currencies and the Euro.

Euros or Cents?

Frequently, a currency calculation is thought of as in major units, but is expected to be accurate in minor units. In this case, it may well be better to perform the calculation in minor units, converting only when necessary. There's an output routine at Cent Number To Euro String.

JavaScript is exact for calculations using only integers not bigger than 9007199254740992.

Working with integers avoids many problems.

See also Currency Rounding below.

Input Range

A general-purpose routine should be capable of handling all possible values of its main argument - including undefined, null, ±0, ±Infinity, NaN, and very large - and should give a reasonable result in every case. It must NEVER give a plausible but incorrect answer.

Because of floating-point rounding errors, any code used to round all non-negative numbers should allow for slightly negative numbers practically equivalent to zero.

For reliable results in the desired format, it is generally necessary that the output, ignoring the decimal point and sign, should be an integer no greater than 253  = 9007199254740992. Routines differ in their treatment of large numbers.

If it is actually necessary to get an unreasonable number of decimal places, add them in the manner of ChrsTo.

#.##5 to #.## - up or down?

Note that a number of the format #.##5, except for #.125, #.375, #.625, #.875, cannot be represented exactly as an IEEE Double, which is what JavaScript uses for numbers. It is therefore often considered acceptable, when converting to two decimal places, for other numbers of the format #.##5 to be rounded either up or down; and similarly for any other "half-way" case of rounding to a decimal.

If this matters, for example when working with £.s.d. including halfpence or farthings, then do the arithmetic with smaller units; integer arithmetic is exact up to 253.

My function StrS rounds the "odd eighths" away from zero; "Bankers' Rounding" would make the final digit even. In the tests, the "Bankers'" line uses inputs represented exactly in an IEEE Double. See also Bankers' Rounding.

Symmetry

When rounding (or truncating) a number which may be either positive or negative, it is sometimes necessary to round either towards plus infinity or towards minus infinity. But those are special cases.

Normally, however, rounding should be symmetrical about zero; rounding X should give the same digits as rounding -X, for all X.

Some of the routines quoted on these pages for signed numbers are unsymmetrical.

Round/Ceil/Floor

One may put ceil / floor instead of round in the code of the following pages. But those can, in the presence of the inevitable rounding errors of non-integer arithmetic, lead to results other than those wanted. Some care is needed; see in Rounding 3.

Multinationalisation

Some algorithms for combining conversion to a formatted string with choice of decimal separator seem error-prone. It may be better to convert the separator afterwards, with for example .replace(/\./, ',').

Input conversion may then also be needed :- parseFloat(Value.replace(/,/, '.')) & +Value.replace(/,/, '.') will read both '0.03' and '0,03' as 3/100. But beware of incoming "thousands" separators.

For adding thousands separators, see in JavaScript Maths.

In my coding, I assume the UK decimal point and no thousands separators.

Testing

The testing code shown here, from an Include file, is used in other pages.

Rounding to a string must be carefully tested. In extreme cases, an undesired layout might be acceptable; but an numerically-incorrect value can never be.

Many routines fail to handle one or more of 0.007, 0.07, NaN, +Infinity, -Infinity, null, undefined well.

For general rounding to two decimal places, tests equivalent to at least the following and their negatives are needed.

The cases 3.965, 3.995 may round differently when negative; #.995 specifically tests for one possible problem.

The test code preserves null and undefined.

Other test numbers are generated algorithmically in TryRnd.

The Test Buttons

The "=" buttons test the current function, using the arguments to its left.

The "Cases" buttons pop-up the test case array, RndCases.

The "cf.toF" buttons show, on a fresh page or tab, a full-range comparison with toFixed(2). Note that toFixed gives errors in MS IE 8 and earlier.

The Code

The conversion functions on the following pages take input from their arguments and use return for the result. Arguments are expected to all be present, though in some cases they can be omitted (and are likely to default to zero). "Size" arguments are expected to be non-negative - after all, they are usually given directly as literals. Those wanting different behaviour should add the requisite code.

Demonstrations

The functions have not all been fully tested with illegal inputs; that will be reserved for those which seem among the best for legal ones. In actual use, it should be natural or easy to ensure that the arguments are numbers or strings representing numbers. Be careful with strings and String Objects; strings beginning with zero may be taken as representing octal numbers. If needful, write a wrapper to normalise input.

N.B. Generally useful functions are now in include1.js. There is, in code and test, by intent, no additional protection against parameter error or absence (absent parameters are converted to 0 by the test process). Background colours are, more or less, significant.

Rounding functions here take one to three arguments, X M N. The first, X, is the quantity to be rounded, M is the number of characters before the decimal point, and N is the number of digits after the decimal point. Sometimes the second argument is different.

It has been reported that an error in the browser Konqueror causes incorrect results.

Imports

The following is imported (from include1.js; see Include Files) and used on the following pages :-

There may be others not shown on these pages; RSVP.

Testing a Reader-Provided Function

Complete Function

Provide in the green TextArea a JavaScript function with up to three parameters; provide parameters in the small boxes; press "=" to get the result.

To enlarge the TextArea, press "=" after setting its first line to
function X() { return ++document.forms.FrmD.Text.rows }

Body of Function

Provide in the green TextArea a body for the JavaScript function Thingy; provide parameters in the small boxes; press "=" to get the results, in the Form and in DivE.

function Thingy(X, M, N) { // A function rounding X, M, N  }
PreE

To set the TextArea to N rows, set its first line to
return document.forms.FrmE.Text.rows = N
and press "=".

Rounding of Approximate Quantities

For precise work, with simple arithmetic, the smallest applicable units should be used (Euros or Cents?) so that addition, subtraction, and multiplication within range are exact.

If the "ideal" value is, say, 1.03, but there may be accumulated rounding errors smaller than 0.005, one can (as in StrU) multiply by 100, use Math.round, and insert a point to get "1.03" rather than perhaps "1.02999999977".

But a Number of value nominally such as 1.035, which cannot be held exactly (see Precision), may need to be rounded to a String with two decimal places as if the Number had been held exactly.

One can multiply by 10N+K, round to the nearest integer, convert to string, and adjust the string. That involves reasonable assumptions, controlled by K, about the maximum rounding error existing in the input.

Rounding, Bankers' Rounding, and Truncation can all be handled by a single routine with the operation selected by an argument.

Function ApRnd2(X, M, N) should match StrU, rounding the intended value of X and giving M digits before the decimal point and N digits after it. With it, routines corresponding to StrS StrT StrW can be produced similarly.

Rounding by String Manipulation

One can use the default conversion of Number to String, and proceed by (tedious) string manipulation.

This may be right; needs checking & tidying; not recommended for use. Bankers' is probably exact, in terms of string S; X can be String.

Function StRnd2(X, M, N) should match StrU, rounding String(X) and giving M digits before the decimal point and N digits after it. With it, routines corresponding to StrS StrT StrW can be produced similarly.

Currency Rounding

Function toCash

Converts approximate pence to pounds and pence.

 
 
 


JavaScript Rounding   1, 2, 3.
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.