Every time I needed a random number in JavaScript, I always ended up building some weird, difficult to read manipulation of the Math.random() method to get a value in the range I needed. After doing this a few times on a few different projects, I decided it would be a worthwhile effort to concoct an all-in-one JavaScript random number generator.

This function accepts between 2 and 5 arguments, specifying the lower bound, upper bound, number of decimal places the returned random should have, and whether or not the upper and lower bounds will be inclusive.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function Rand(lowerBound,upperBound,decimalPlaces,lowerInclusive,upperInclusive) { if (typeof(decimalPlaces) == "boolean" && typeof(upperInclusive) == "undefined") { upperInclusive = lowerInclusive; lowerInclusive = decimalPlaces; decimalPlaces = 0; } decimalPlaces = decimalPlaces === undefined ? 0 : decimalPlaces; lowerInclusive = lowerInclusive === undefined ? 0 : lowerInclusive === true ? 0 : 1 / Math.pow(10, decimalPlaces); upperInclusive = upperInclusive === undefined ? lowerInclusive : upperInclusive === true ? 0 : 1 / Math.pow(10, decimalPlaces); return Math.floor(((Math.random() * ((upperBound - upperInclusive) - (lowerBound + lowerInclusive) + (1 / Math.pow(10, decimalPlaces)))) + (lowerBound + lowerInclusive)) * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces); } |

[*Enough! Just let me try it alredy!*]

## Breaking Down the Arguments

The hurdle this function is simply determining which arguments have been passed and how to interpret them. I wanted to build this so it was both flexible and intuitive, and would handle a variety of formats for its arguments.

- The first 2 arguments are required and will always be considered the lower and upper bound, respectively. These arguments will accept positive or negative numbers (or 0), and may be integers or floats.
- The third argument (and all others after it) may be left out, in which case the number of decimal places of the returned random will be zero, and both bounds will be assumed to be inclusive.
- If the third argument is a number, it will be used as the number of decimal places the returned random should have.
**This argument accepts negative values!**If you want a random multiple of 10 set this argument to -1, for a random multiple of 100, set it to -2, etc. - If the third argument is a boolean, it is treated like the fourth argument, the fourth argument is treated like the fifth, and the fifth is ignored.
- The fourth argument, if passed, is a boolean determining whether the lower bound is inclusive. The default is true.
- If the a fourth argument is specified and the fifth is not, then the fourth argument is assumed to represent both the upper and lower bounds.
- The fifth argument, if passed, is a boolean determining whether the upper bound is inclusive. The default is true.

#### Examples:

Rand(3,19)

Returns a random number with 0 decimal places between 3 (inclusive) and 19 (inclusive).

Rand(0,5,2)

Returns a random number with 2 decimal places between 0 (inclusive) and 5 (inclusive).

Rand(0,20,false)

Returns a random number with 0 decimal places between 0 (non-inclusive) and 20 (non-inclusive).

Rand(5,15,true,false)

Returns a random number with 0 decimal places between 5 (inclusive) and 15 (non-inclusive).

Rand(0,2000,-2,false)

Returns a random multiple of 100 between 0 (non-inclusive) and 2000 (non-inclusive).

Rand(10,15,5,false,true)

Returns a random number with 5 decimal places between 10 (non-inclusive) and 15 (inclusive).

## How it Works

The first if statement deals with the possibility that the decimal places have been left out, and a boolean has been found as the third argument. This sets the value of decimalPlaces to 0, the value of lowerInclusive to the third argument, and the value of upperInclusive to the fourth argument.

1 2 3 4 5 |
if (typeof(decimalPlaces) == "boolean" && typeof(upperInclusive) == "undefined") { upperInclusive = lowerInclusive; lowerInclusive = decimalPlaces; decimalPlaces = 0; } |

Before going on to the next section of the code, it’s important to understand how the inclusivity of the upper and lower bounds are used. When the Math.random() is finally called, it will be using bounds that are both inclusive. So in order to feed into that, the bounds passed in the first two arguments are adjusted based on the number of decimal places. For instance, if the decimal places are set to 2, and 5 is the lower bound but should be non-inclusive, then 5.01 will be used as the lower bound.

In order to accomplish this, if the inclusive arguments are true, they’re then set to 0. If they’re false, they’re set to 1/10^{decimalPlaces}. Then the lowerInclusive value will be added to the lower bound, and the upperInclusive value will be subtracted from the upperBound.

1 2 3 4 5 |
decimalPlaces = decimalPlaces === undefined ? 0 : decimalPlaces; lowerInclusive = lowerInclusive === undefined ? 0 : lowerInclusive === true ? 0 : 1 / Math.pow(10, decimalPlaces); upperInclusive = upperInclusive === undefined ? lowerInclusive : upperInclusive === true ? 0 : 1 / Math.pow(10, decimalPlaces); |

The last block of this is kinda scary. I’ll try to break it out into a (slightly) more readable form (or at least one where I can reference line numbers):

1 2 3 4 5 6 7 8 9 10 11 12 13 |
Math.floor( ( ( Math.random() * ( (upperBound - upperInclusive) - (lowerBound + lowerInclusive) + (1 / Math.pow(10, decimalPlaces)) ) ) + (lowerBound + lowerInclusive) ) * Math.pow(10, decimalPlaces) ) / Math.pow(10, decimalPlaces); |

OK, so. First, let’s concentrate on lines 5 – 7. In this range, we’re determining what we’ll multiply the result of Math.random() by. Since Math.random() returns a value between 0 and 1, we need to multiply it by something in order to cover our range. Essentially, if we have a range of 5 (as in upperBound – lowerBound == 5) we’d need to multiply Math.random() by 5. Then we’ll have a random value between 0 and 5, which we can further manipulate to get to our goal.

Since this is going to be based around a Math.floor() eventually, we also need to add 1 / Math.pow(10, decimalPlaces) to our multiplier. This is what allows the upper end of the spectrum to be included. Imagine the values 0 and 1 have been passed as bounds, inclusive and with no decimals. Our range between the two numbers is 1, and if we multiplied our Math.random() result by 1, we’d still get a number less than 1 every time. Then if we ran a Math.floor() we’d always end up with 0. By adding 1 / Math.pow(10, decimalPlaces), the highest possible value will be reachable.

Next, on lines 9 and 10, we add the lowerInclusive + lowerBound to the result of our multiplier * Math.random(). This is to move our random range into the proper position. if we have a random range of 0 – 5, but our lower bound was 7 and our upper bound was 12, then we need to add 7 to our number to fall within the expected range.

Then on line 11 we multiply again, this time by Math.pow(10, decimalPlaces). We’ll divide by the same total in a minute to get back to our appropriate range and number of decimal places, but first we need this multiplier so the Math.floor() we’re about to perform doesn’t cut off any significant figures (I know that’s a bastardization of a statistical term, but I can’t think of any better way of describing it succinctly here).

Lines 12 and 13 close the Math.floor()function that wraps everything we’ve looked at so far, and then divides the whole thing by Math.pow(10, decimalPlaces). The resulting number should perfectly fit the parameters laid out in the arguments, and while it’s not a true random number, it doesn’t lose any of the randomness of the Math.random() function itself.

## Try it Out

Rand( , , , , );

## Leave a Reply