4

How can I get a random float value from /dev/urandom?

If I simply use a cast, by saying:

int fd = ::open("/dev/urandom", O_RDONLY);
uint32_t n;
read(fd, &n, sizeof(n));
float f = n;

...I'm not sure if I have a guarantee of portability, because I don't know if large values of n will necessarily be representable as f? Is MAXUINT guaranteed to be representable as a float?

Channel72
  • 24,139
  • 32
  • 108
  • 180
  • 1
    I have strong doubts that the numbers you build in this fashion will be _uniformly distributed_ -- you should probably stick to an interface such as `drand48()` for building random floating point numbers. – sarnold Jun 26 '12 at 22:39
  • 1
    What range of numbers do you want? The usual way is to do something like this question describes: http://stackoverflow.com/questions/686353/c-random-float (Using `/dev/[u]random`, as you suggest, might be better that calling `rand()`, but the concept is the same.) Using `drand48()` as @sarnold suggests looks like a good solution, too - maybe better. – mpontillo Jun 26 '12 at 22:40
  • 1
    @Mike: the downside to `drand48()` is that it uses a linear congruential algorithm -- suitable for scientific and most game uses but not suitable for cryptography-level work. `/dev/urandom` re-fills an entropy pool during use -- but the format of floating point numbers makes me skeptical about using raw bits to populate every field of the number... – sarnold Jun 26 '12 at 22:47
  • 1
    @sarnold: The value of `n` will be uniformly distributed over the range 0 .. 4294967295. Assigning it to `f` will probably lose some precision, but it should still be pretty well uniformly distributed. Whether you really *want* floating-point values in the range 0 .. 4294967295 is another question. – Keith Thompson Jun 26 '12 at 23:59
  • 1
    The question cannot be meaningfully answered unless you define what you mean by a "random float value". Uniform distribution or something else? What range? – Keith Thompson Jun 27 '12 at 00:02
  • "_Is MAXUINT guaranteed to be representable as a float?_" if you mean "**exactly** representable", then that's the only part of your question that is well defined and that can be answered, and the answer is extremely obvious where `unsigned int` has the same number of significant bits as `float`. As Keith Thompson said, you need to reword your question in term of values distribution, not just in term of C datatype. – curiousguy Jun 27 '12 at 14:04
  • Also, you need to explicitly state your architecture assumptions: do you deal with IEEE floats? – curiousguy Jun 27 '12 at 14:17
  • @curiousguy: The answer is far from "extremely obvious", and your hint does nothing to make it any more obvious: what do you mean by "significant bits"? And just out of curiosity: what do _you_ think the asnwer is? – TonyK Apr 05 '13 at 16:22
  • @curiousguy: So we are agreed that the largest possible `unsigned int` can't be represented exactly by a `float`. Thank goodness for that! But your reasoning is faulty: it is not necessary that _all_ integers up to `2**n-1` be representable in order for _some_ of them to be representable. For instance, `2**(n-1)` is representable as a `float`. The fact that `2**n-1` is not representable depends on the binary representation of floating-point numbers, not on a mindless counting of bits. – TonyK Apr 06 '13 at 21:42
  • @TonyK "_So we are agreed that the largest possible unsigned int can't be represented exactly by a float._" Yes indeed. I am removing my previous comments as they are not much useful. – curiousguy Apr 06 '13 at 21:54

3 Answers3

5

You get random bytes from /dev/urandom, but those bytes won't necessarily form a) uniformly distributed floating point values or b) even legal representations of whatever object you treat them as. For example on a platform that has trapping representations of floats or ints then there'll be the possibility that the values you create will be trapping representations, and you won't be able to actually use the random values you create.

You should simply verify that your library's implementation of std::random_device() allows access to /dev/urandom (either by default or by taking a string argument like: std::random_device("/dev/urandom"). Then you have a random number engine that can be used with, for example, std::uniform_real_distribution<float>() in order to get the random number distribution you want.

✓ libstdc++ uses /dev/urandom by default:

✓ libc++ does as well:

✗ Visual Studio's implementation is not even using a non-deterministic RNG:

✓ As of VS2012 MSDN states "the values produced by default are non-deterministic and cryptographically secure," probably via Windows' Cryptographic Services.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • 1
    What a surprise. GNU libstdc++ has it right, so does LLVM libc++, but Visual Studio can't even get close. – Linuxios Jun 26 '12 at 23:05
  • 1
    Oh come on; what trap representations are there going to be for a `uint32_t`? And assigning that to a `float` isn't going to cause a problem either. Furthermore, assuming `/dev/urandom` is uniform, then the floating-point values are going to be as uniform as it's possible to get, surely? – Oliver Charlesworth Jun 27 '12 at 00:32
  • @OliCharlesworth There won't be trap representations for uint32_t, but there could be for int. Concatenating uniformly distributed bytes will produce uniformly distributed two's complement or signed magnitude integers, but it will not produce uniformly distributed IEEE 754 floats. Consider that there are the same number of IEEE 754 floating point representations between 1 and 2 as between 2 and 4. If you get an integer and convert it to float you won't have that distortion, but you will have the distortion that ints above a certain value will be 'rounded' to some other integral value. – bames53 Jun 27 '12 at 00:50
  • Of course, but that's only important if you're reinterpreting the bits as a `float`. The OP is merely assigning to a `float`. I can't imagine a way to get a more uniform distribution of `float`s in the range [-2^32, +2^32). – Oliver Charlesworth Jun 27 '12 at 00:54
  • It seems highly unlikely to me that you'd want to represent integral values in the range of uint32_t as floats. I think the OP was simply trying to get float values, without intending to be limited to that particular distribution. Whatever the desired distribution, it's much clearer and less error prone to express via `` distributions. – bames53 Jun 27 '12 at 01:24
  • @bames53 "_Concatenating uniformly distributed bytes will produce uniformly distributed two's complement or signed magnitude integers, but it will not produce uniformly distributed IEEE 754 floats._" Actually **it will produce uniformly distributed IEEE 754 floats** which is probably not what anyone might want! – curiousguy Jun 27 '12 at 14:08
  • @curiousguy what I mean by that is that it will not produces values uniformly distributed across the range of real values representable by floats. But yes, it will produce every representation of IEEE 754 floats with equal probability, and yeah, nobody wants that. – bames53 Jun 27 '12 at 14:13
  • @bames53 why have you written that VS implementation does not use non-deterministic RNG? Documentation that you've linked, says _The class describes a source of random numbers, preferably from a *non-deterministic external device*. In this implementation the values produced by default are *non-deterministic and cryptographically secure*_ Did they change documentation since the moment you've written that? – notsurewhattodo Apr 05 '13 at 14:37
  • @MariuszPluciński Yes, they've changed the documentation since I posted that. – bames53 Apr 05 '13 at 14:40
0

It is possible to read random bytes directly into a float (e.g. float f; read(fd, &f, sizeof(f));); assuming IEEE-754, all bit-patterns are valid (although there are two infinities and many quiet and signalling NaNs). You can use ieee754.h or fpclassify.h to determine whether you've hit one of those.

Of course, whether this is reasonable depends on what kind of distribution you want. Assuming IEEE-754 float32, you'll get a uniform distribution across and in each range of [0, 2-126) denormals and [2-126, 2-125), [2-125, 2-124), ..., [2127, 2128) normals (and their negations).

ephemient
  • 198,619
  • 38
  • 280
  • 391
  • This would give you a highly skewed (and therefore probably totally unusable) distribution. – Oliver Charlesworth Jun 27 '12 at 00:25
  • @OliCharlesworth It gives you a roughly exponential distribution. I explained this in the answer. If you want to cover *all* the reals, what other kind of distribution do you want? – ephemient Jun 27 '12 at 01:23
  • "_Assuming IEEE-754 float32, you'll get a uniform distribution across and in each range of [0, 2-126) denormals and [2-126, 2-125), [2-125, 2-124), ..., [2127, 2128) normals (and their negations)._" and a few NaN and infinities, which could test for if you don't want them – curiousguy Jun 27 '12 at 14:13
  • @OliCharlesworth "_This would give you a highly skewed (and therefore probably totally unusable) distribution_" but it's a **uniform distribution** of IEEE-754 float values (probably not what the OP intended). The question is not well defined anyway. How can this answer get 1 downvote when the meaningless question gets 4 upvotes? That's crazy. – curiousguy Jun 27 '12 at 14:15
0

You can get a random number floating point by dividing a random integer through the maximum integer value.

unsigned int RAND_MAX = 0xffffffff; // for 32 bit values
unsigned int random_value = rand_function();
double rand = (double) random_value / (double) RAND_MAX;

That random number will be between 0.0 and 1.0. If you want it to be in a certain range just do it like this:

double x = 1.0; // range start
double y = 3.5; // range end
double range = rand * (y - x) + x; // creates values between 1.0 and 3.5
  • Hmmm. `RAND_MAX` is defined in `` and it is an `int`, not `unsigned int` like this answer - suggest another macro name. Typically it has a value like `0x7fffffff`. – chux - Reinstate Monica Mar 21 '17 at 22:20