@@ -67,18 +67,25 @@ impl fmt::Display for Error {
6767impl std:: error:: Error for Error { }
6868
6969impl Geometric {
70- /// Construct a new `Geometric` with the given shape parameter `p`
71- /// (probability of success on each trial).
70+ /// Construct a new `Geometric` distribution
71+ ///
72+ /// The shape parameter `p` is the probability of success on each trial.
73+ ///
74+ /// ### Edge cases
75+ ///
76+ /// If `p == 0.0` or `1.0 - p` rounds to `1.0` then sampling returns
77+ /// `u64::MAX`.
7278 pub fn new ( p : f64 ) -> Result < Self , Error > {
79+ let mut pi = 1.0 - p;
7380 if !p. is_finite ( ) || !( 0.0 ..=1.0 ) . contains ( & p) {
7481 Err ( Error :: InvalidProbability )
75- } else if p == 0 .0 || p >= 2.0 / 3.0 {
76- Ok ( Geometric { p, pi : p , k : 0 } )
82+ } else if pi == 1 .0 || p >= 2.0 / 3.0 {
83+ Ok ( Geometric { p, pi, k : 0 } )
7784 } else {
7885 let ( pi, k) = {
7986 // choose smallest k such that pi = (1 - p)^(2^k) <= 0.5
8087 let mut k = 1 ;
81- let mut pi = ( 1.0 - p ) . powi ( 2 ) ;
88+ pi = pi * pi ;
8289 while pi > 0.5 {
8390 k += 1 ;
8491 pi = pi * pi;
@@ -106,7 +113,7 @@ impl Distribution<u64> for Geometric {
106113 return failures;
107114 }
108115
109- if self . p == 0 .0 {
116+ if self . pi == 1 .0 {
110117 return u64:: MAX ;
111118 }
112119
@@ -264,4 +271,18 @@ mod test {
264271 fn geometric_distributions_can_be_compared ( ) {
265272 assert_eq ! ( Geometric :: new( 1.0 ) , Geometric :: new( 1.0 ) ) ;
266273 }
274+
275+ #[ test]
276+ fn small_p ( ) {
277+ let a = f64:: EPSILON / 2.0 ;
278+ assert ! ( 1.0 - a < 1.0 ) ; // largest repr. value < 1
279+ assert ! ( Geometric :: new( a) . is_ok( ) ) ;
280+
281+ let b = f64:: EPSILON / 4.0 ;
282+ assert ! ( b > 0.0 ) ;
283+ assert ! ( 1.0 - b == 1.0 ) ; // rounds to 1
284+ let d = Geometric :: new ( b) . unwrap ( ) ;
285+ let mut rng = crate :: test:: VoidRng ;
286+ assert_eq ! ( d. sample( & mut rng) , u64 :: MAX ) ;
287+ }
267288}
0 commit comments