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