Skip to content

Commit 6e82023

Browse files
committed
Binomial: Comment on difference to original algorithm
1 parent f6d7407 commit 6e82023

File tree

1 file changed

+19
-7
lines changed

1 file changed

+19
-7
lines changed

src/distributions/binomial.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ impl Binomial {
4646
}
4747
}
4848

49+
/// Convert a `f64` to an `i64`, panicing on overflow.
50+
// In the future (Rust 1.34), this might be replaced with `TryFrom`.
51+
fn f64_to_i64(x: f64) -> i64 {
52+
assert!(x < (::std::i64::MAX as f64));
53+
x as i64
54+
}
55+
4956
impl Distribution<u64> for Binomial {
5057
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 {
5158
// Handle these values directly.
@@ -105,7 +112,7 @@ impl Distribution<u64> for Binomial {
105112
let np = n * p;
106113
let npq = np * q;
107114
let f_m = np + p;
108-
let m = f_m as i64;
115+
let m = f64_to_i64(f_m);
109116
// radius of triangle region, since height=1 also area of region
110117
let p1 = (2.195 * npq.sqrt() - 4.6 * q).floor() + 0.5;
111118
// tip of triangle
@@ -138,7 +145,7 @@ impl Distribution<u64> for Binomial {
138145
let u = rng.gen_range(0., p4);
139146
let mut v = rng.gen_range(0., 1.);
140147
if !(u > p1) {
141-
y = (x_m - p1 * v + u) as i64;
148+
y = f64_to_i64(x_m - p1 * v + u);
142149
break;
143150
}
144151

@@ -150,19 +157,19 @@ impl Distribution<u64> for Binomial {
150157
if v > 1. {
151158
continue;
152159
} else {
153-
y = x as i64;
160+
y = f64_to_i64(x);
154161
}
155162
} else if !(u > p3) {
156163
// Step 3: Region 3, left exponential tail.
157-
y = (x_l + v.ln() / lambda_l) as i64;
164+
y = f64_to_i64(x_l + v.ln() / lambda_l);
158165
if y < 0 {
159166
continue;
160167
} else {
161168
v *= (u - p2) * lambda_l;
162169
}
163170
} else {
164171
// Step 4: Region 4, right exponential tail.
165-
y = (x_r - v.ln() / lambda_r) as i64;
172+
y = f64_to_i64(x_r - v.ln() / lambda_r);
166173
if y > 0 && (y as u64) > self.n {
167174
continue;
168175
} else {
@@ -222,8 +229,8 @@ impl Distribution<u64> for Binomial {
222229
// Step 5.3: Final acceptance/rejection test.
223230
let x1 = (y + 1) as f64;
224231
let f1 = (m + 1) as f64;
225-
let z = ((n as i64) + 1 - m) as f64;
226-
let w = ((n as i64) - y + 1) as f64;
232+
let z = (f64_to_i64(n) + 1 - m) as f64;
233+
let w = (f64_to_i64(n) - y + 1) as f64;
227234

228235
fn stirling(a: f64) -> f64 {
229236
let a2 = a * a;
@@ -233,6 +240,11 @@ impl Distribution<u64> for Binomial {
233240
if alpha > x_m * (f1 / x1).ln()
234241
+ (n - (m as f64) + 0.5) * (z / w).ln()
235242
+ ((y - m) as f64) * (w * p / (x1 * q)).ln()
243+
// We use the signs from the GSL implementation, which are
244+
// different than the ones in the reference. According to
245+
// the GSL authors, the new signs were verified to be
246+
// correct by one of the original designers of the
247+
// algorithm.
236248
+ stirling(f1) + stirling(z) - stirling(x1) - stirling(w)
237249
{
238250
continue;

0 commit comments

Comments
 (0)