Skip to main content

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}