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}