4

I need a repeatable pseudo-random function from floats in [0,1] to floats in [0,1]. I.e. given a 32-bit IEEE float, return a "different" one (as random as possible, given the 24 bits of mantissa). It has to be repeatable, so keeping tons of internal state is out. And unfortunately it has to work with only 32-bit int and single-float math (no doubles and not even 32x32=64bit multiply, though I could emulate that if needed -- basically it needs to work on older CUDA hardware). The better the randomness the better, of course, within these rather severe limitations. Anyone have any ideas?

(I've been through Park-Miller, which requires 64-bit int math, and the CUDA version of Park-Miller which requires doubles, Mersenne Twisters which have lots of internal state, and a few other things which didn't work.)

GaryO
  • 5,873
  • 1
  • 36
  • 61
  • 1
    _Tons of internal state_ doesn't imply _not reproducible_. Do you strictly need a function that returns a `float` given a `float` and the correlation between the two is difficult to discover? Or are you really looking for a good pseudo-random number generator with limited computation power? – sarnold May 31 '11 at 22:58
  • Should the function be invertible or not? Does it need to be cryptographically strong? – caf Jun 01 '11 at 03:57
  • Invertible? No. Crypto-strong? No. I'm doing this for graphics purposes so I need reasonably well-distributed results. Basically given an (x,y) as floats I want my function to return a new pseudo-random (x,y) point as a function only of the original x,y. Or same in 1 dimension: given x, return x' where x' "looks" random. By repeatable I mean result has to be purely a function of the input. – GaryO Jun 01 '11 at 13:42

3 Answers3

3

The NVIDIA CUDA Toolkit includes a library called CURAND that I believe fits your requirements: it produces repeatable results (assuming you start with the same seed), works on the GPU, supports 32-bit floats and ints, and should work on older GPUs. It also supports multiple pseudo- and quasi-random generation algorithms and distributions.

[Note: a problem with using the C library rand() function (other than that it does not run in CUDA on the device) is that on Windows, rand() only returns a 16-bit value, and thus any float created by division by RAND_MAX has only 16 random bits of precision. What's more, on linux/mac it returns a 32-bit value so code that uses it is not numerically portable.]

harrism
  • 26,505
  • 2
  • 57
  • 88
  • Your answer is correct as I posed the problem -- thanks. Unfortunately I left out one requirement; I need the same values when running on the CPU as on GPU, so a CUDA-only lib doesn't work for me. That's why I've been trying to do it myself, so I can get portable (GPU/CPU) code. – GaryO Jun 01 '11 at 13:38
  • The implementation of the curand device API is pretty clear in curand_kernel.h -- you could pretty trivially port that to work on the CPU. As a start, you might just try changing the #define QUALIFIERS in that file so that instead of `#define QUALIFIERS static inline __device__` you have `#define QUALIFIERS static inline __device__ __host__`. That should make all the functions you call from device kernels callable from host code. (There may be other problems, I haven't tried this). – harrism Jun 02 '11 at 04:58
  • Alternatively, you could use the curand host API to generate an array of random values on the GPU, then copy the array back to the host for use in CPU code. – harrism Jun 02 '11 at 04:59
3

Best I understand the requirements, a hash accomplishes the desired functionality. Re-interprete the float input as an integer, apply the hash function to produce an integer approximately uniformly distributed in [0,2^32), then multiply this integer by 2^-32 to convert the resulting integer back to a float roughly uniformly distributed in [0,1]. One suitable hash function which does not require multiplication is Bob Jenkin's mix(), which can be found here: http://www.burtleburtle.net/bob/hash/doobs.html.

To re-interpret the bits of a float as an integer and vice versa, there are two choices in CUDA. Use intrinsics, or use C++-style reinterpretation casts:

float f;
int i;
i = __float_as_int(f);
f = __int_as_float(i);
i = reinterpret_cast<int&>(f);
f = reinterpret_cast<float&>(i);

So as a self-contained function, the entire process might look something like this:

/* transform float in [0,1] into a different float in [0,1] */
float scramble_float (float f)
{
    unsigned int magic1 = 0x96f563ae; /* number of your choice */
    unsigned int magic2 = 0xb93c7563; /* number of your choice */
    unsigned int j;
    j = reinterpret_cast<unsigned int &>(f);
    mix (magic1, magic2, j);
    return 2.3283064365386963e-10f * j;
}
njuffa
  • 23,970
  • 4
  • 78
  • 130
  • Cool! Definitely not the way I was thinking before, but right on target. – GaryO Jun 02 '11 at 23:25
  • 1
    This turned out to be a great direction to go in. I ended up using two rounds of a different but excellent mix function (1-input 32-bit no-collision) by Jenkins I found at http://home.comcast.net/~bretm/hash/3.html which is too large to paste here, and gives fine results. – GaryO Jun 04 '11 at 10:45
2

Why not use the standard C library rand() function and divide the result by RAND_MAX?

#include <stdlib.h>
float randf (void)
{
     return rand() / (float) RAND_MAX;
}
wallyk
  • 56,922
  • 16
  • 83
  • 148
  • Don't forget to seed with `srand` before calling `rand`. – Zach Rattner Jun 01 '11 at 00:40
  • Unfortunately, doesn't work on CUDA. But on the CPU at least you're right that you can cast the float bits to int i=*(int *)&f; and then use that to seed srand. – GaryO Jun 01 '11 at 13:37