 # JavaScript Rounding Recipes

Very often calculations in JavaScript produce results that don’t quite fall within the ranges we want, or generate numbers that need to be “cleaned up” before use. Exactly what happens to those numbers - rounding up or down, set within a range, or being “clipped” to a certain number of decimal places - depends on what you want to use them for.

## Why Round Numbers?

One of the curious aspects of JavaScript is that it doesn’t actually store integers: instead, it thinks of numbers as floating point binaries. This, combined with the fact that many fractions can’t be expressed in a finite number of decimal places, means that JavaScript can create results like the following (using the console):

``````0.1 * 0.2;
> 0.020000000000000004``````

For practical purposes, this imprecision won’t matter in the vast majority of cases - we’re talking about an error of 2 quintillionths - but it is a little frustrating. It can also introduce slightly odd-looking results when dealing with numbers that represent values of currencies, percentages, or file sizes. To fix these representations, we need to round the results, or set decimal precision.

Rounding numbers has many other practical applications: if a user is manipulating a `range` element, for example, we may want to round the associated value to the nearest whole number, rather than dealing with a decimal.

## Rounding Decimal Numbers

To clip a decimal number, use the `toFixed()` or `toPrecision` methods. Both take a single argument that determines, respectively, how many significant digits (i.e. the total number of digits used in the number) or decimal places (the number of digitals after the decimal point) to include in the result:

• If no argument is specified for `toFixed()`, the default is 0, i.e. no decimal places; the argument has a maximum value of 20.
• if no argument is specified for `toPrecision`, the number is unchanged.
``````var randNum = 6.25;
randNum.toFixed();
> "6"

Math.PI.toPrecision(1);
> "3"

var randNum = 87.335;
randNum.toFixed(2);
> "87.33"

var randNum = 87.337;
randNum.toPrecision(3);
> "87.3"``````

`toFixed()` and `toPrecision()` are also useful methods to pad out a whole number to include decimal places, which can be especially handy when dealing with numbers that represent currencies:

``````var wholeNum = 1
var dollarsCents = wholeNum.toFixed(2);
console.log(dollarsCents);
> "1.00"``````

## Avoiding Decimal Rounding Errors

In some cases, both `toFixed` and `toPrecision` will round values of `5` down rather than up:

``````var numTest = 1.005;
numTest.toFixed(2);
> 1;``````

The result of the calculation above should be `1.01`, not `1`. If avoiding this error is important, I’d recommend a solution suggested by Jack L Moore that uses exponential numbers for the calculation:

``````function round(value, decimals) {
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}``````

To use:

``````round(1.005,2);
> 1.01``````

## Epsilon Rounding

An alternative method of rounding decimals was introduced with ES6 (aka JavaScript 2015). “Machine epsilon” provides a reasonable margin of error when comparing two floating point numbers. Without rounding, comparisons can yield results like the following:

``````0.1 + 0.2 === 0.3
> false``````

`Math.EPSILON` can be used in a function to produce correct comparisons:

``````function epsEqu(x, y) {
return Math.abs(x - y) < Number.EPSILON * Math.max(Math.abs(x), Math.abs(y));
}``````

The function takes two arguments: one carrying the calculation, the other the (rounded) expected result. It returns the comparison of the two:

``````epsEqu(0.1 + 0.2, 0.3)
> true``````

## Truncating Decimal Numbers

All the techniques shown so far do some kind of rounding to decimal numbers. To truncate a positive number to two decimal places, multiply it by 100, truncate it, then divide the result by 100:

``````function truncated(num) {
return Math.trunc(num * 100) / 100;
}

truncated(3.1416)
> 3.14``````

If you want something with a bit more adaptability, you can take advantage of the bitwise double tilde operator:

``````function truncated(num, decimalPlaces) {
var numPowerConverter = Math.pow(10, decimalPlaces);
return ~~(num * numPowerConverter)/numPowerConverter;
}``````

To use:

``````var randInt = 35.874993;
truncated(randInt,3);
> 35.874``````

I’ll have more to say about bitwise operations in a future article.

## Rounding To The Nearest Number

To round a decimal number up or down to the nearest whole number, depending on which is closest, use `Math.round()`:

``````Math.round(4.3)
> 4

Math.round(4.5)
> 5``````

Note that “half values” like `.5` round up.

## Rounding Down To The Nearest Whole Number

If you always want to round down, use `Math.floor`:

``````Math.floor(42.23);
> 42

Math.floor(36.93);
> 36``````

Note that this “down” rounding direction is true for all numbers, including negatives. Think of a skyscraper with an infinite number of floors, including basement levels (representing the negative numbers). If you’re in an elevator between basement levels 2 and 3 (represented by a value of -2.5), `Math.floor` will take you to -3:

``````Math.floor(-2.5);
> -3``````

If want to avoid this behaviour, use `Math.trunc`, supported in all modern browsers (except IE/Edge):

``````Math.trunc(-41.43);
> -41``````

## Rounding Up To The Nearest Whole Number

Alternatively, if you always want to round up, use `Math.ceil`. Again, think of that infinite elevator: `Math.ceil` will always go “upwards”, regardless of whether the number is negative or not:

``````Math.ceil(42.23);
> 43

Math.ceil(36.93);
> 37``````
Math.ceil(-36.93); -36

## Rounding Up/Down To The Nearest Multiple of a Number

If we want to round to the nearest multiple of 5, the easiest way is to create a function that divides the number by 5, rounds it, then multiplies it by the same amount:

``````function roundTo5(num) {
return Math.round(num/5)*5;
}``````

To use it:

``````roundTo5(11);
> 10``````

If you were rounding to multiples of different numbers, we could make this function more general by passing the function both the initial number and the multiple:

``````function roundToMultiple(num, multiple) {
return Math.round(num/multiple)*multiple;
}``````

To use it, include both the number and the multiple in the call to the function:

``````
var initialNumber = 11;
var multiple = 10;
roundToMultiple(initialNumber, multiple);
> 10;``````

To exclusively round up or round down; substitute `ceil` or `floor` for `round` in the function.

## Clamping Number To a Range

There are plenty of times when you may receive a value `x` that needs to be within the bounds of a range. For example, you may need a value from `1` to `100`, but receive a value of `123`. To fix this, we can use `min` (which will always return the `smallest` of a set of numbers) and `max` (the largest member of any set of numbers). Using the `1` to `100` example:

``````var lowBound = 1;
var highBound = 100;
var numInput = 123;
var clamped = Math.max(lowBound, Math.min(numInput, highBound));
console.log(clamped);
> 100;``````

Again, this could be turned into a function, or possibly an extension of the `Number` class, a variation first suggested by Daniel X. Moore:

``````Number.prototype.clamp = function(min, max) {
return Math.min(Math.max(this, min), max);
};``````

To use it:

``(numInput).clamp(lowBound, highBound);``

## Gaussian Rounding

Gaussian rounding, also known as “bankers” rounding, convergent rounding, Dutch rounding, and odd–even rounding, is a method of rounding without statistical bias; regular rounding has a native upwards bias. Gaussian rounding avoids this by rounding to the nearest even number. The best solution I’m aware of is by Tim Down:

``````function gaussRound(num, decimalPlaces) {
var d = decimalPlaces || 0,
m = Math.pow(10, d),
n = +(d ? num * m : num).toFixed(8),
i = Math.floor(n), f = n - i,
e = 1e-8,
r = (f > 0.5 - e && f < 0.5 + e) ?
((i % 2 == 0) ? i : i + 1) : Math.round(n);
return d ? r / m : r;
}``````

Examples in use:

``````gaussRound(2.5)
> 2

gaussRound(3.5)
> 4

gaussRound(2.57,1)
> 2.6``````

Image by Philippe Put, used under a Creative Commons license