# Are rand(Float32) really uniform in \[0,1)?

**URL:** <https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585>\
**Category:** General Usage\
**Tags:** question\
**Created:** [October 31, 2025, 11:03pm UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585 "2025-10-31T23:03:53Z")\
**Posts on this page:** 10\
**Page:** 2

<div class="post-metadata">

**Author:** ![nhz2](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nhz2/32/44428_2.png) [@nhz2](https://discourse.julialang.org/u/nhz2)\
**Post date:** [November 3, 2025, 8:11pm UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/21 "2025-11-03T20:11:04Z")

</div>

My current go-to way to sample a uniform distribution is with `randneg11() = fma(rand(Int64), 2.0^-63, 2.0^-64)` ported from [Random123](https://github.com/DEShawResearch/random123/blob/726a093cd9a73f3ec3c8d7a70ff10ed8efec8d13/include/Random123/uniform.hpp#L186). Instead of going from [0, 1), it goes [-1, 1], which is often more useful. The mean is exactly zero, but the output is never exactly zero, so no divide-by-zero errors.

In general, RNG’s are good at generating uniform Int64 or UInt64. How or if you want to convert to floating point is always going to be kind of problem-specific.

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [November 4, 2025, 11:20am UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/22 "2025-11-04T11:20:18Z")

</div>

> [@mbauman](#):
>
> `rand(Float32) < p` has probability p of being true for all p that are multiples of 2^{-24} in [0, 1]

I’d like to offer two slight modifications:

1. We don’t need the probability to be exactly p. It suffices if the probability is statistically indistinguishable from p up to some generous amount of samples, e.g. 2^120 samples (2^120 samples far exceeds the amount of computation that is accessible to mankind, and far exceeds the reliability of hardware).
2. The big potential improvement that was discussed was whether we want the stronger property that `rand(Float32) < p` has probability indistinguishable from p for all p with `p == Float32(p)`. As mbaumann noted, this is the same thing in (0.5, 1); but it is a very different thing in (0, 2^{-30}).

Some applications need the stronger property (2). A very practical example is when you want to sample from pareto/power-law distributions via `1/rand()`; or, more generally, apply a transformation to the sample that has a singularity at 0.

If you apply a transformation to a floating point number with singularity different from zero, then you deserve what you get. However, floating point numbers are designed to handle singularities near zero or near infinity – thats the point of floating the point, “relative error” – so one might naively expect that from random samples.

---

<div class="post-metadata">

**Author:** ![FattiMei](https://avatars.discourse-cdn.com/v4/letter/f/5f9b8f/32.png) [@FattiMei](https://discourse.julialang.org/u/FattiMei)\
**Post date:** [November 4, 2025, 2:36pm UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/23 "2025-11-04T14:36:11Z")

</div>

Can we instead do P(rand(Float32) = x) = \texttt{nextfloat}(x) - x for all representable x \in [0,1)?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [November 4, 2025, 2:55pm UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/24 "2025-11-04T14:55:55Z")

</div>

> [@foobar\_lv2](#):
>
> when you want to sample from

This is precisely the distinction. Modern floating point `rand()` implementations in practically all languages _do not sample from_ a continuous Uniform distribution over [0, 1). You really shouldn’t transform them as though they did.

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [November 4, 2025, 3:15pm UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/25 "2025-11-04T15:15:43Z")

</div>

> [@FattiMei](#):
>
> Can we instead do P(rand(Float32) = x) = \texttt{nextfloat}(x) - x for all representable x \in [0,1)?

That’s what the infinite-precision algorithm [linked to by an above post](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/19) achieves.

To save some notation: `nextfloat(x)-x == eps(x)` (for `x >= 0` except `floatmax`).

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [November 4, 2025, 5:04pm UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/26 "2025-11-04T17:04:29Z")

</div>

> [@FattiMei](#):
>
> Can we instead do P(rand(Float32) = x) = \texttt{nextfloat}(x) - x for all representable x \in [0,1)?

It’s worth pointing out that the denominator-corrected `CppRNG` doesn’t accomplish this distribution despite the biased integer-to-float map and more uniform usage of mantissa bits because the nominal and minimum resolution is 2^-32, much larger than the smallest normal `floatmin(Float32) == 1.1754944f-38` and the subnormals. The formula does neatly express that some floating point values x are generated more often than others and implies P(rand(Float32) \leq x) = \texttt{nextfloat}(x) , a rounding down of a standard uniform distribution.

> [@mbauman](#):
>
> Modern floating point `rand()` implementations in practically all languages _do not sample from_ a continuous Uniform distribution over [0, 1). You really shouldn’t transform them as though they did.

But it is _approximating_ one, right? If so, are there really no continuous transformations we can adapt? Or is there just some hopefully intuitive limit to the math? Does this change if we use the biased map instead?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [November 4, 2025, 6:03pm UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/27 "2025-11-04T18:03:47Z")

</div>

> [@Benny](#):
>
> But it is _approximating_ one, right? If so, are there really no continuous transformations we can adapt?

Yes, exactly — it’s a _rounded down_ approximation to a **fixed** finite precision, which is not the same as the precision of the float it returns (and I do agree, that’s unfortunate and might be unexpected, but it matches everyone else).

But here’s the powerful thing about having this particular definition. It means that, even though there are only a handful **discrete values** we may see from `log(1-rand(Float32))` that are less than -15, _I’m pretty sure I still know how to rigorously interpret them_! And I can in fact build a CDF atop that. It’s just not a _random variable_ out of the log-transformed distribution.

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [November 4, 2025, 6:06pm UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/28 "2025-11-04T18:06:17Z")

</div>

> [@mbauman](#):
>
> Modern floating point `rand()` implementations in practically all languages _do not sample from_ a continuous Uniform distribution over [0, 1). You really shouldn’t transform them as though they did.

Yes. We are in full agreement.

I just think that this is very unfortunate, because it would be natural to expect to be able to do this. And people actually make this mistake, like the linked rust package.

I think we’re missing “sample floating-point-uniform from `(0,1)`” as a feature/API, linked from the `rand()` docstring.

> [@Benny](#):
>
> But it is _approximating_ one, right? If so, are there really no continuous transformations we can adapt?

The issue is that most of the software world – including julia – aim for something like |P(rand() \< p) - p| \< \epsilon, i.e. absolute tolerances, as appropriate for fixed point numbers.

On the other hand, floating point is specifically designed to often allow estimates of the style |P(rand() \< p) - p| \< \epsilon \max(P(rand()\<p), p), i.e. relative error estimates and tolerances.

Relative tolerances are ingrained into the intuitions of many people and scientific fields. People who are not aware of the history of this API are liable to simply assume bounded relative error of probabilities for floating point sampling. Otherwise it should be called “fixed point sampling”.

And no, it is not possible to fix that afterwards, by doing transforms. Lost precision cannot be recovered. Nor is it possible to solve this via a better UInt32-\>Float32 map: Probabilities of UInt32 are quantized to integer multiples of 2^-32, but small floats need much lower probabilities. Instead, one needs to either introduce branches, or needs to overprovision random bits (e.g. consume 128 random bits to generate a single 32bit float, without branches).

However, this is a pretty niche requirement that can very well be a separate API.

---

<div class="post-metadata">

**Author:** ![abraemer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraemer/32/51403_2.png) [@abraemer](https://discourse.julialang.org/u/abraemer)\
**Post date:** [November 4, 2025, 8:26pm UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/29 "2025-11-04T20:26:30Z")

</div>

> [@foobar\_lv2](#):
>
> I think we’re missing “sample floating-point-uniform from `(0,1)`” as a feature/API, linked from the `rand()` docstring.

I think that’s actually a great solution to this whole discussion! We could just take one of the proposals from the previous thread and make a function `Random.randfloat` that produces a random float following the uniform distribution on `(0,1)`(or including 1 idc). Together with a warning+link from `rand`’s docstring, that seems to be a quite satisfactory solution to me 🙂

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [November 5, 2025, 9:15am UTC](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585/30 "2025-11-05T09:15:18Z")

</div>

> [@abraemer](#):
>
> We could just take one of the proposals from the previous thread and make a function `Random.randfloat` that produces a random float following the uniform distribution on `(0,1)`(or including 1 idc)

Or, alternatively, a package could introduce a wrapper type, eg `Uniformly` and then define `rand(rng, ::Type{Uniformly{T}}) where T <: AbstractFloat`.

> [@mbauman](#):
>
> Modern floating point `rand()` implementations in practically all languages _do not sample from_ a continuous Uniform distribution over [0, 1). You really shouldn’t transform them as though they did.

I think that the best solution would be **documenting** what users can expect from `rand(rng, ::Type{<:AbstractFloat})`. Pseudorandom number generation is not intuitive, and is also subject to practical constraints (for most people, a fast implementation is preferable over some “ideal” one, even if we could agree on the latter).

Probably saying that

1. `rand(T) < p` has a probability p \pm \delta(T), where \delta(\text{Float32}) = 2^{-24}, and similarly for other float types, in a table,
2. \delta(T) for a specific type will not increase in future Julia versions,

would be a good starting point.

This

1. documents the current behavior,
2. yet leaves room for further optimization/changes,
3. clarifies that if you do transformations that are sensitive to smaller values, you may be introducing a mistake (eg a naive `log2(rand(Float32))`).

[Previous page](https://discourse.julialang.org/t/are-rand-float32-really-uniform-in-0-1/133585.md?page=1)
