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