Coverage Report

Created: 2026-07-13 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rand-0.8.6/src/distributions/float.rs
Line
Count
Source
1
// Copyright 2018 Developers of the Rand project.
2
//
3
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6
// option. This file may not be copied, modified, or distributed
7
// except according to those terms.
8
9
//! Basic floating-point number distributions
10
11
use crate::distributions::utils::FloatSIMDUtils;
12
use crate::distributions::{Distribution, Standard};
13
use crate::Rng;
14
use core::mem;
15
16
#[cfg(feature = "serde1")]
17
use serde::{Serialize, Deserialize};
18
19
/// A distribution to sample floating point numbers uniformly in the half-open
20
/// interval `(0, 1]`, i.e. including 1 but not 0.
21
///
22
/// All values that can be generated are of the form `n * ε/2`. For `f32`
23
/// the 24 most significant random bits of a `u32` are used and for `f64` the
24
/// 53 most significant bits of a `u64` are used. The conversion uses the
25
/// multiplicative method.
26
///
27
/// See also: [`Standard`] which samples from `[0, 1)`, [`Open01`]
28
/// which samples from `(0, 1)` and [`Uniform`] which samples from arbitrary
29
/// ranges.
30
///
31
/// # Example
32
/// ```
33
/// use rand::{thread_rng, Rng};
34
/// use rand::distributions::OpenClosed01;
35
///
36
/// let val: f32 = thread_rng().sample(OpenClosed01);
37
/// println!("f32 from (0, 1): {}", val);
38
/// ```
39
///
40
/// [`Standard`]: crate::distributions::Standard
41
/// [`Open01`]: crate::distributions::Open01
42
/// [`Uniform`]: crate::distributions::uniform::Uniform
43
#[derive(Clone, Copy, Debug)]
44
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
45
pub struct OpenClosed01;
46
47
/// A distribution to sample floating point numbers uniformly in the open
48
/// interval `(0, 1)`, i.e. not including either endpoint.
49
///
50
/// All values that can be generated are of the form `n * ε + ε/2`. For `f32`
51
/// the 23 most significant random bits of an `u32` are used, for `f64` 52 from
52
/// an `u64`. The conversion uses a transmute-based method.
53
///
54
/// See also: [`Standard`] which samples from `[0, 1)`, [`OpenClosed01`]
55
/// which samples from `(0, 1]` and [`Uniform`] which samples from arbitrary
56
/// ranges.
57
///
58
/// # Example
59
/// ```
60
/// use rand::{thread_rng, Rng};
61
/// use rand::distributions::Open01;
62
///
63
/// let val: f32 = thread_rng().sample(Open01);
64
/// println!("f32 from (0, 1): {}", val);
65
/// ```
66
///
67
/// [`Standard`]: crate::distributions::Standard
68
/// [`OpenClosed01`]: crate::distributions::OpenClosed01
69
/// [`Uniform`]: crate::distributions::uniform::Uniform
70
#[derive(Clone, Copy, Debug)]
71
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
72
pub struct Open01;
73
74
75
// This trait is needed by both this lib and rand_distr hence is a hidden export
76
#[doc(hidden)]
77
pub trait IntoFloat {
78
    type F;
79
80
    /// Helper method to combine the fraction and a constant exponent into a
81
    /// float.
82
    ///
83
    /// Only the least significant bits of `self` may be set, 23 for `f32` and
84
    /// 52 for `f64`.
85
    /// The resulting value will fall in a range that depends on the exponent.
86
    /// As an example the range with exponent 0 will be
87
    /// [2<sup>0</sup>..2<sup>1</sup>), which is [1..2).
88
    fn into_float_with_exponent(self, exponent: i32) -> Self::F;
89
}
90
91
macro_rules! float_impls {
92
    ($ty:ident, $uty:ident, $f_scalar:ident, $u_scalar:ty,
93
     $fraction_bits:expr, $exponent_bias:expr) => {
94
        impl IntoFloat for $uty {
95
            type F = $ty;
96
            #[inline(always)]
97
0
            fn into_float_with_exponent(self, exponent: i32) -> $ty {
98
                // The exponent is encoded using an offset-binary representation
99
0
                let exponent_bits: $u_scalar =
100
0
                    (($exponent_bias + exponent) as $u_scalar) << $fraction_bits;
101
0
                $ty::from_bits(self | exponent_bits)
102
0
            }
Unexecuted instantiation: <u32 as rand::distributions::float::IntoFloat>::into_float_with_exponent
Unexecuted instantiation: <u64 as rand::distributions::float::IntoFloat>::into_float_with_exponent
103
        }
104
105
        impl Distribution<$ty> for Standard {
106
0
            fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
107
                // Multiply-based method; 24/53 random bits; [0, 1) interval.
108
                // We use the most significant bits because for simple RNGs
109
                // those are usually more random.
110
0
                let float_size = mem::size_of::<$f_scalar>() as u32 * 8;
111
0
                let precision = $fraction_bits + 1;
112
0
                let scale = 1.0 / ((1 as $u_scalar << precision) as $f_scalar);
113
114
0
                let value: $uty = rng.gen();
115
0
                let value = value >> (float_size - precision);
116
0
                scale * $ty::cast_from_int(value)
117
0
            }
Unexecuted instantiation: <rand::distributions::Standard as rand::distributions::distribution::Distribution<f32>>::sample::<_>
Unexecuted instantiation: <rand::distributions::Standard as rand::distributions::distribution::Distribution<f64>>::sample::<_>
118
        }
119
120
        impl Distribution<$ty> for OpenClosed01 {
121
0
            fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
122
                // Multiply-based method; 24/53 random bits; (0, 1] interval.
123
                // We use the most significant bits because for simple RNGs
124
                // those are usually more random.
125
0
                let float_size = mem::size_of::<$f_scalar>() as u32 * 8;
126
0
                let precision = $fraction_bits + 1;
127
0
                let scale = 1.0 / ((1 as $u_scalar << precision) as $f_scalar);
128
129
0
                let value: $uty = rng.gen();
130
0
                let value = value >> (float_size - precision);
131
                // Add 1 to shift up; will not overflow because of right-shift:
132
0
                scale * $ty::cast_from_int(value + 1)
133
0
            }
Unexecuted instantiation: <rand::distributions::float::OpenClosed01 as rand::distributions::distribution::Distribution<f32>>::sample::<_>
Unexecuted instantiation: <rand::distributions::float::OpenClosed01 as rand::distributions::distribution::Distribution<f64>>::sample::<_>
134
        }
135
136
        impl Distribution<$ty> for Open01 {
137
0
            fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> $ty {
138
                // Transmute-based method; 23/52 random bits; (0, 1) interval.
139
                // We use the most significant bits because for simple RNGs
140
                // those are usually more random.
141
                use core::$f_scalar::EPSILON;
142
0
                let float_size = mem::size_of::<$f_scalar>() as u32 * 8;
143
144
0
                let value: $uty = rng.gen();
145
0
                let fraction = value >> (float_size - $fraction_bits);
146
0
                fraction.into_float_with_exponent(0) - (1.0 - EPSILON / 2.0)
147
0
            }
Unexecuted instantiation: <rand::distributions::float::Open01 as rand::distributions::distribution::Distribution<f32>>::sample::<_>
Unexecuted instantiation: <rand::distributions::float::Open01 as rand::distributions::distribution::Distribution<f64>>::sample::<_>
148
        }
149
    }
150
}
151
152
float_impls! { f32, u32, f32, u32, 23, 127 }
153
float_impls! { f64, u64, f64, u64, 52, 1023 }
154
155
156
#[cfg(test)]
157
mod tests {
158
    use super::*;
159
    use crate::rngs::mock::StepRng;
160
161
    const EPSILON32: f32 = ::core::f32::EPSILON;
162
    const EPSILON64: f64 = ::core::f64::EPSILON;
163
164
    macro_rules! test_f32 {
165
        ($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => {
166
            #[test]
167
            fn $fnn() {
168
                // Standard
169
                let mut zeros = StepRng::new(0, 0);
170
                assert_eq!(zeros.gen::<$ty>(), $ZERO);
171
                let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0);
172
                assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0);
173
                let mut max = StepRng::new(!0, 0);
174
                assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0);
175
176
                // OpenClosed01
177
                let mut zeros = StepRng::new(0, 0);
178
                assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), 0.0 + $EPSILON / 2.0);
179
                let mut one = StepRng::new(1 << 8 | 1 << (8 + 32), 0);
180
                assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON);
181
                let mut max = StepRng::new(!0, 0);
182
                assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0);
183
184
                // Open01
185
                let mut zeros = StepRng::new(0, 0);
186
                assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0);
187
                let mut one = StepRng::new(1 << 9 | 1 << (9 + 32), 0);
188
                assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0);
189
                let mut max = StepRng::new(!0, 0);
190
                assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0);
191
            }
192
        };
193
    }
194
    test_f32! { f32_edge_cases, f32, 0.0, EPSILON32 }
195
196
    macro_rules! test_f64 {
197
        ($fnn:ident, $ty:ident, $ZERO:expr, $EPSILON:expr) => {
198
            #[test]
199
            fn $fnn() {
200
                // Standard
201
                let mut zeros = StepRng::new(0, 0);
202
                assert_eq!(zeros.gen::<$ty>(), $ZERO);
203
                let mut one = StepRng::new(1 << 11, 0);
204
                assert_eq!(one.gen::<$ty>(), $EPSILON / 2.0);
205
                let mut max = StepRng::new(!0, 0);
206
                assert_eq!(max.gen::<$ty>(), 1.0 - $EPSILON / 2.0);
207
208
                // OpenClosed01
209
                let mut zeros = StepRng::new(0, 0);
210
                assert_eq!(zeros.sample::<$ty, _>(OpenClosed01), 0.0 + $EPSILON / 2.0);
211
                let mut one = StepRng::new(1 << 11, 0);
212
                assert_eq!(one.sample::<$ty, _>(OpenClosed01), $EPSILON);
213
                let mut max = StepRng::new(!0, 0);
214
                assert_eq!(max.sample::<$ty, _>(OpenClosed01), $ZERO + 1.0);
215
216
                // Open01
217
                let mut zeros = StepRng::new(0, 0);
218
                assert_eq!(zeros.sample::<$ty, _>(Open01), 0.0 + $EPSILON / 2.0);
219
                let mut one = StepRng::new(1 << 12, 0);
220
                assert_eq!(one.sample::<$ty, _>(Open01), $EPSILON / 2.0 * 3.0);
221
                let mut max = StepRng::new(!0, 0);
222
                assert_eq!(max.sample::<$ty, _>(Open01), 1.0 - $EPSILON / 2.0);
223
            }
224
        };
225
    }
226
    test_f64! { f64_edge_cases, f64, 0.0, EPSILON64 }
227
228
    #[test]
229
    fn value_stability() {
230
        fn test_samples<T: Copy + core::fmt::Debug + PartialEq, D: Distribution<T>>(
231
            distr: &D, zero: T, expected: &[T],
232
        ) {
233
            let mut rng = crate::test::rng(0x6f44f5646c2a7334);
234
            let mut buf = [zero; 3];
235
            for x in &mut buf {
236
                *x = rng.sample(&distr);
237
            }
238
            assert_eq!(&buf, expected);
239
        }
240
241
        test_samples(&Standard, 0f32, &[0.0035963655, 0.7346052, 0.09778172]);
242
        test_samples(&Standard, 0f64, &[
243
            0.7346051961657583,
244
            0.20298547462974248,
245
            0.8166436635290655,
246
        ]);
247
248
        test_samples(&OpenClosed01, 0f32, &[0.003596425, 0.73460525, 0.09778178]);
249
        test_samples(&OpenClosed01, 0f64, &[
250
            0.7346051961657584,
251
            0.2029854746297426,
252
            0.8166436635290656,
253
        ]);
254
255
        test_samples(&Open01, 0f32, &[0.0035963655, 0.73460525, 0.09778172]);
256
        test_samples(&Open01, 0f64, &[
257
            0.7346051961657584,
258
            0.20298547462974248,
259
            0.8166436635290656,
260
        ]);
261
    }
262
}