rand_float/uniform.rs
1//! The technique of choice, plus integration with the [`rand`] crate.
2//!
3//! This module is an alias for [`pekkizen`], whose
4//! leading-zeros technique provides complete coverage of [0 . . 1) at
5//! almost the cost of the 53-bit [`division`] scaling, consuming one
6//! 64-bit word per call with probability 1 − 2⁻¹².
7//!
8//! The [`unif_01`] function converts any source of random 64-bit words;
9//! with the `rand` feature (enabled by default), the [`Unif01Ext`]
10//! extension trait additionally endows every generator implementing
11//! [`rand_core::Rng`] with the same conversion as a [`unif_01`][method]
12//! method.
13//!
14//! [`rand`]: https://crates.io/crates/rand
15//! [`pekkizen`]: crate::pekkizen
16//! [`division`]: crate::division
17//! [method]: Unif01Ext::unif_01
18
19pub use crate::pekkizen::*;
20
21/// Returns a random `f64` distributed as a uniform real in [0 . . 1)
22/// rounded down to the nearest representable value: every float in
23/// [0 . . 1) is reachable, subnormals and 0 included, with probability
24/// equal to the measure of the reals that round down to it.
25///
26/// An alias for [`f64_full`], the technique of choice; the `rand` feature
27/// provides the same conversion as a method ([`Unif01Ext::unif_01`]).
28///
29/// # Examples
30///
31/// ```
32/// let mut src = rand_float::sources::Weyl(42);
33/// let x = rand_float::uniform::unif_01(|| src.next_u64());
34/// assert!((0.0..1.0).contains(&x));
35/// ```
36#[inline(always)]
37pub fn unif_01(bits: impl FnMut() -> u64) -> f64 {
38 f64_full(bits)
39}
40
41/// Extension trait adding a [`unif_01`][method] method to every generator
42/// implementing [`rand_core::Rng`] (in particular, to the generators of the
43/// [`rand`] crate).
44///
45/// [`rand`]: https://crates.io/crates/rand
46/// [method]: Self::unif_01
47#[cfg(feature = "rand")]
48pub trait Unif01Ext {
49 /// Returns a random `f64` distributed as a uniform real in [0 . . 1)
50 /// rounded down to the nearest representable value: every float in
51 /// [0 . . 1) is reachable, subnormals and 0 included, with probability
52 /// equal to the measure of the reals that round down to it.
53 ///
54 /// This is [`f64_full`] applied to the generator.
55 ///
56 /// # Examples
57 ///
58 /// ```
59 /// use rand_float::uniform::Unif01Ext;
60 ///
61 /// let x = rand::rng().unif_01();
62 /// assert!((0.0..1.0).contains(&x));
63 /// ```
64 fn unif_01(&mut self) -> f64;
65}
66
67#[cfg(feature = "rand")]
68impl<R: rand_core::Rng + ?Sized> Unif01Ext for R {
69 #[inline(always)]
70 fn unif_01(&mut self) -> f64 {
71 f64_full(|| self.next_u64())
72 }
73}
74
75#[cfg(all(test, feature = "rand"))]
76mod tests {
77 use super::*;
78 use crate::sources::Weyl;
79
80 /// [`Weyl`] as a [`rand_core::Rng`] (via an infallible
81 /// [`rand_core::TryRng`]), for testing only.
82 struct WeylRng(Weyl);
83
84 impl rand_core::TryRng for WeylRng {
85 type Error = core::convert::Infallible;
86
87 fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
88 Ok(self.0.next_u64() as u32)
89 }
90
91 fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
92 Ok(self.0.next_u64())
93 }
94
95 fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Self::Error> {
96 for chunk in dst.chunks_mut(8) {
97 let bytes = self.0.next_u64().to_le_bytes();
98 chunk.copy_from_slice(&bytes[..chunk.len()]);
99 }
100 Ok(())
101 }
102 }
103
104 /// Method and function form of `unif_01` must agree with `f64_full`
105 /// on the same word stream.
106 #[test]
107 fn test_unif_01_matches_f64_full() {
108 let mut rng = WeylRng(Weyl(42));
109 let mut src = Weyl(42);
110 let mut src2 = Weyl(42);
111 for _ in 0..100_000 {
112 let expected = f64_full(|| src.next_u64());
113 assert_eq!(rng.unif_01(), expected);
114 assert_eq!(unif_01(|| src2.next_u64()), expected);
115 }
116 }
117}