bpo-24567: Random subnormal.diff (#7954) · python/cpython@ddf7171 (original) (raw)

3 files changed

lines changed

Original file line number Diff line number Diff line change
@@ -383,7 +383,9 @@ def choices(self, population, weights=None, *, cum_weights=None, k=1):
383 383 raise ValueError('The number of weights does not match the population')
384 384 bisect = _bisect.bisect
385 385 total = cum_weights[-1]
386 -return [population[bisect(cum_weights, random() * total)] for i in range(k)]
386 +hi = len(cum_weights) - 1
387 +return [population[bisect(cum_weights, random() * total, 0, hi)]
388 +for i in range(k)]
387 389
388 390 ## -------------------- real-valued distributions -------------------
389 391
Original file line number Diff line number Diff line change
@@ -227,6 +227,14 @@ def test_choices(self):
227 227 with self.assertRaises(IndexError):
228 228 choices([], cum_weights=[], k=5)
229 229
230 +def test_choices_subnormal(self):
231 +# Subnormal weights would occassionally trigger an IndexError
232 +# in choices() when the value returned by random() was large
233 +# enough to make `random() * total` round up to the total.
234 +# See https://bugs.python.org/msg275594 for more detail.
235 +choices = self.gen.choices
236 +choices(population=[1, 2], weights=[1e-323, 1e-323], k=5000)
237 +
230 238 def test_gauss(self):
231 239 # Ensure that the seed() method initializes all the hidden state. In
232 240 # particular, through 2.2.1 it failed to reset a piece of state used
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
1 +Improve random.choices() to handle subnormal input weights that could
2 +occasionally trigger an IndexError.