I've previously written an algorithm that generates random floats in any [a,b], which can generate all possible floating point values, including subnormals, with the proper probability, and does so quickly for any choice of a and b. [0]
Thanks for sharing this. I was worried when I saw the rejection test at the end (around line 1749) that this could be slow, but I guess that for cases like a=0 and b=1, your code avoids that rejection ever happening, is that right? So the rejection only kicks in when the bounds are in the middle of range of one fixed exponent?
The rejection case should only ever be executed for ranges that differ in more than one exponent. (This is handles by the cases above)
The generated exponent starts of at the max exponent of a and b, and is logarithmically decremented (I'm not sure if this is the correct word for it, but decremented until a coin flip hits tails, would be a better way to explain it).
So the exponent and the fraction range can't ever get really small, since there is always at least one exponent that accepts the full range of fractions.
Edit: Actually, looking at it again, the second special case doesn't apply to subnormals, so while generating in the range
float x = 2.3509886E-38f; // exp=00000001,frac=11111111111111111111111
float min = nextafterf(nextafterf(x, 0), 0);
float max = nextafterf(nextafterf(x, 1), 1);
works almost instantly (on average 4 iterations), the current code does indeed have a bottle neck for:
float x = 1.1754942E-38f; // exp=00000000,frac=11111111111111111111111
float min = nextafterf(nextafterf(x, 0), 0);
float max = nextafterf(nextafterf(x, 1), 1);
It's not insanely-minutes-long-stall-bad, but takes about 0.02 seconds to generate one number in the above range with a good prng (opposed to 2e-08 seconds for the first case)
I'll look into fixing this.
Edit 2: I've added a commited a comment about this for now. I wonder why my test suite didn't catch this.
[0] https://github.com/camel-cdr/cauldron/blob/main/cauldron/ran...