jfcalvo
jfcalvo

Reputation: 485

Get a pseudorandom float number in a closed interval [a, b]

I would like to know the best way to get a pseudorandom float number in a closed interval using the Ruby rand kernel function (please not Random module).

To take an example I will use the closed interval [0.0, 7.7] (both 0.0 and 7.7 included in the interval), but any other float interval should be valid too.

For the interval [0.0, 7.7] the next solution is not valid:

rand * 7.7

Why?

If you call rand without arguments you will get a pseudorandom floating point number greater than or equal to 0.0 and less than 1.0. So what is the range of float numbers that the previous solutions can give to us?

rand will return a pseudorandom float number in the range [0.0, 0.9999999...]

0.0 * 7.7
=> 0.0          # Correct!

0.9999999 * 7.7
=> 7.69999923   # Incorrect!

The interval does not match with [0.0, 7.7].

Does anyone know an elegant solution to this problem?

Thank you!

Upvotes: 1

Views: 1362

Answers (5)

Jeremy E
Jeremy E

Reputation: 3704

Depending on the number of digits of precision you need you could use a round to even approach to snap to the boundaries of the interval to the edges. Hope this helps.

Here is the text from Wikipedia

Round half to evenA tie-breaking rule that is even less biased is round half to even, namely

If the fraction of y is 0.5, then q is the even integer nearest to y. Thus, for example, +23.5 becomes +24, +22.5 becomes +22, −22.5 becomes −22, and −23.5 becomes −24.

This method also treats positive and negative values symmetrically, and therefore is free of overall bias if the original numbers are positive or negative with equal probability. In addition, for most reasonable distributions of y values, the expected (average) value of the rounded numbers is essentially the same as that of the original numbers, even if the latter are all positive (or all negative). However, this rule will still introduce a positive bias for even numbers (including zero), and a negative bias for the odd ones.

This variant of the round-to-nearest method is also called unbiased rounding (ambiguously, and a bit abusively), convergent rounding, statistician's rounding, Dutch rounding, Gaussian rounding, or bankers' rounding. This is widely used in bookkeeping.

This is the default rounding mode used in IEEE 754 computing functions and operators.

Upvotes: 0

user12861
user12861

Reputation: 2426

Why do you need this? I don't know of a case where there would be a need for this as a true single or double precision number. On the other hand, there are real cases where you might need numbers between 0.0 and 7.7 in increments of 0.1. In that case you could use well established techniques to go from 0 to 77 and then divide by 10.

Upvotes: 0

David Weiser
David Weiser

Reputation: 5195

How about:

(rand/0.9999999999999999...)*7.7

Basically, normalize the random number by the largest possible random number. That way you will create the range [0..1].

However, I am unsure how to get the max number, which is less than 1.0 in ruby.

Upvotes: 0

David Grayson
David Grayson

Reputation: 87396

I would do something like this:

Fineness = 2**64
puts rand(Fineness+1)*7.7/Fineness

Whenever rand returns its maximum possible value, you will get Fineness*7.7/Fineness which turns out to equal 7.7 exactly (but I'm not totally sure this will always be the case, because floats are inexact).

As long as Fineness has more bits in it than a double on your computer, then I believe you will not notice any strangeness in the distribution of your results.

Upvotes: 0

Mon ouïe
Mon ouïe

Reputation: 958

There's a Random class that can do what you want:

generator = Random.new # You need to instance it
generator.rand 0.0..7.7

(The documentation states the difference between 0.0..7.7 and 0.0...7.7 will be taken in account.)

In the future 1.9.3, you'll be able to pass a range to Kernel#rand and Random.rand (you can already do that in the preview version).

Upvotes: 4

Related Questions