summaryrefslogtreecommitdiff
path: root/numpy/random
diff options
context:
space:
mode:
authorgfyoung <gfyoung17@gmail.com>2017-03-26 14:30:28 -0700
committergfyoung <gfyoung17@gmail.com>2017-05-09 03:12:52 -0400
commit1f88730b3140af1efb7d2e789d21078ad66c8eee (patch)
treeb5bf76ed7ce34652098fd65e99e59a94337c5c98 /numpy/random
parent23b0cf3a5925dbc1f0503fbdacdf8088372f4ab5 (diff)
downloadpython-numpy-1f88730b3140af1efb7d2e789d21078ad66c8eee.tar.gz
python-numpy-1f88730b3140af1efb7d2e789d21078ad66c8eee.tar.bz2
python-numpy-1f88730b3140af1efb7d2e789d21078ad66c8eee.zip
BUG: Buttress handling of extreme values in randint
Diffstat (limited to 'numpy/random')
-rw-r--r--numpy/random/mtrand/mtrand.pyx21
-rw-r--r--numpy/random/tests/test_random.py45
-rw-r--r--numpy/random/tests/test_regression.py9
3 files changed, 60 insertions, 15 deletions
diff --git a/numpy/random/mtrand/mtrand.pyx b/numpy/random/mtrand/mtrand.pyx
index 9f901f14c..c0082a782 100644
--- a/numpy/random/mtrand/mtrand.pyx
+++ b/numpy/random/mtrand/mtrand.pyx
@@ -964,20 +964,31 @@ cdef class RandomState:
high = low
low = 0
+ # '_randint_type' is defined in
+ # 'generate_randint_helpers.py'
key = np.dtype(dtype).name
- if not key in _randint_type:
+ if key not in _randint_type:
raise TypeError('Unsupported dtype "%s" for randint' % key)
+
lowbnd, highbnd, randfunc = _randint_type[key]
- if low < lowbnd:
+ # TODO: Do not cast these inputs to Python int
+ #
+ # This is a workaround until gh-8851 is resolved (bug in NumPy
+ # integer comparison and subtraction involving uint64 and non-
+ # uint64). Afterwards, remove these two lines.
+ ilow = int(low)
+ ihigh = int(high)
+
+ if ilow < lowbnd:
raise ValueError("low is out of bounds for %s" % (key,))
- if high > highbnd:
+ if ihigh > highbnd:
raise ValueError("high is out of bounds for %s" % (key,))
- if low >= high:
+ if ilow >= ihigh:
raise ValueError("low >= high")
with self.lock:
- ret = randfunc(low, high - 1, size, self.state_address)
+ ret = randfunc(ilow, ihigh - 1, size, self.state_address)
if size is None:
if dtype in (np.bool, np.int, np.long):
diff --git a/numpy/random/tests/test_random.py b/numpy/random/tests/test_random.py
index e10c442a5..0e7396494 100644
--- a/numpy/random/tests/test_random.py
+++ b/numpy/random/tests/test_random.py
@@ -157,22 +157,42 @@ class TestRandint(TestCase):
for dt in self.itype:
lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min
ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1
+
tgt = ubnd - 1
assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt)
+
tgt = lbnd
assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt)
+
tgt = (lbnd + ubnd)//2
assert_equal(self.rfunc(tgt, tgt + 1, size=1000, dtype=dt), tgt)
+ def test_full_range(self):
+ # Test for ticket #1690
+
+ for dt in self.itype:
+ lbnd = 0 if dt is np.bool_ else np.iinfo(dt).min
+ ubnd = 2 if dt is np.bool_ else np.iinfo(dt).max + 1
+
+ try:
+ self.rfunc(lbnd, ubnd, dtype=dt)
+ except Exception as e:
+ raise AssertionError("No error should have been raised, "
+ "but one was with the following "
+ "message:\n\n%s" % str(e))
+
def test_in_bounds_fuzz(self):
# Don't use fixed seed
np.random.seed()
+
for dt in self.itype[1:]:
for ubnd in [4, 8, 16]:
vals = self.rfunc(2, ubnd, size=2**16, dtype=dt)
assert_(vals.max() < ubnd)
assert_(vals.min() >= 2)
- vals = self.rfunc(0, 2, size=2**16, dtype=np.bool)
+
+ vals = self.rfunc(0, 2, size=2**16, dtype=np.bool_)
+
assert_(vals.max() < 2)
assert_(vals.min() >= 0)
@@ -209,6 +229,29 @@ class TestRandint(TestCase):
res = hashlib.md5(val).hexdigest()
assert_(tgt[np.dtype(np.bool).name] == res)
+ def test_int64_uint64_corner_case(self):
+ # When stored in Numpy arrays, `lbnd` is casted
+ # as np.int64, and `ubnd` is casted as np.uint64.
+ # Checking whether `lbnd` >= `ubnd` used to be
+ # done solely via direct comparison, which is incorrect
+ # because when Numpy tries to compare both numbers,
+ # it casts both to np.float64 because there is
+ # no integer superset of np.int64 and np.uint64. However,
+ # `ubnd` is too large to be represented in np.float64,
+ # causing it be round down to np.iinfo(np.int64).max,
+ # leading to a ValueError because `lbnd` now equals
+ # the new `ubnd`.
+
+ dt = np.int64
+ tgt = np.iinfo(np.int64).max
+ lbnd = np.int64(np.iinfo(np.int64).max)
+ ubnd = np.uint64(np.iinfo(np.int64).max + 1)
+
+ # None of these function calls should
+ # generate a ValueError now.
+ actual = np.random.randint(lbnd, ubnd, dtype=dt)
+ assert_equal(actual, tgt)
+
def test_respect_dtype_singleton(self):
# See gh-7203
for dt in self.itype:
diff --git a/numpy/random/tests/test_regression.py b/numpy/random/tests/test_regression.py
index b50b6b260..ce435b374 100644
--- a/numpy/random/tests/test_regression.py
+++ b/numpy/random/tests/test_regression.py
@@ -55,15 +55,6 @@ class TestRegression(TestCase):
b = np.random.permutation(long(12))
assert_array_equal(a, b)
- def test_randint_range(self):
- # Test for ticket #1690
- lmax = np.iinfo('l').max
- lmin = np.iinfo('l').min
- try:
- random.randint(lmin, lmax)
- except:
- raise AssertionError
-
def test_shuffle_mixed_dimension(self):
# Test for trac ticket #2074
for t in [[1, 2, 3, None],