Repository: HypothesisWorks/hypothesis

Issue:
st.integers(min_value=0, max_value=1e100) almost only returns large numbers
```
hypothesis.__version__
Out[70]: '3.66.0'
a = [st.integers(min_value=0, max_value=1e100).example() for x in range(999)]
len([x for x in a if x >= 1e97])
Out[72]: 997
len([x for x in a if x < 1e97])
Out[73]: 2
len([x for x in a if x < 1e94])
Out[74]: 0
```
The same happens with floats(). As a workaround I'm currently using filter, which gives a better distribution
```
a = [st.integers().filter(lambda x: x > 0 and x < 1e100).example() for x in range(999)]
len([x for x in a if x >= 1e97])
Out[75]: 0
len([x for x in a if x < 1e97])
Out[76]: 999
```
I'm on Ubuntu 16.04 using python 2.7.


Discussion:
First and most generally, `.example()` gives a highly biased distribution - it's designed to show illustrative rather than representative examples.

That said, considering this alongside #909 and #1212 makes me thing that we may well have some issues with data variety from such strategies.  (though note again that it's not specified what *will* be generated, just that some things *could* be generated and that we try to find bugs)
Thanks for your reply. I was just trying to make the example as simple as possible. I first noticed the issue when a test case with ```@given(st.floats(min_value=-1e100, max_value=1e100))``` didn't find a bug in one of my functions that only occurred with numbers relatively close to 0. Whereas the filter alternative did.
I've just been looking at `integers()` for #1616, and the root cause is that we delegate from `BoundedIntStrategy` directly down to `cu.integer_range` which is basically a uniform distribution.  Instead, we probably want to check if the range is large (e.g. >16-bit) and if so use something more like the psudeo-geometric approach from `WideRangeIntStrategy`.

Resolving patch:
diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/utils.py b/hypothesis-python/src/hypothesis/internal/conjecture/utils.py
--- a/hypothesis-python/src/hypothesis/internal/conjecture/utils.py
+++ b/hypothesis-python/src/hypothesis/internal/conjecture/utils.py
@@ -93,6 +93,14 @@ def integer_range(data, lower, upper, center=None):
     bits = bit_length(gap)
     probe = gap + 1
 
+    if bits > 24 and data.draw_bits(3):
+        # For large ranges, we combine the uniform random distribution from draw_bits
+        # with the weighting scheme used by WideRangeIntStrategy with moderate chance.
+        # Cutoff at 2 ** 24 so unicode choice is uniform but 32bit distribution is not.
+        idx = Sampler([4.0, 8.0, 1.0, 1.0, 0.5]).sample(data)
+        sizes = [8, 16, 32, 64, 128]
+        bits = min(bits, sizes[idx])
+
     while probe > gap:
         data.start_example(INTEGER_RANGE_DRAW_LABEL)
         probe = data.draw_bits(bits)


Test patch:
diff --git a/hypothesis-python/tests/nocover/test_integer_ranges.py b/hypothesis-python/tests/nocover/test_integer_ranges.py
--- a/hypothesis-python/tests/nocover/test_integer_ranges.py
+++ b/hypothesis-python/tests/nocover/test_integer_ranges.py
@@ -19,8 +19,10 @@
 
 import pytest
 
+from hypothesis import given
 from hypothesis.internal.conjecture.utils import integer_range
 from hypothesis.searchstrategy.strategies import SearchStrategy
+from hypothesis.strategies import integers
 from tests.common.debug import minimal
 
 
@@ -43,3 +45,19 @@ def test_intervals_shrink_to_center(inter):
         assert minimal(s, lambda x: x < center) == center - 1
     if center < upper:
         assert minimal(s, lambda x: x > center) == center + 1
+
+
+def test_bounded_integers_distribution_of_bit_width_issue_1387_regression():
+    values = []
+
+    @given(integers(0, 1e100))
+    def test(x):
+        values.append(x)
+
+    test()
+
+    # We draw from a shaped distribution up to 128bit ~7/8 of the time, and
+    # uniformly the rest.  So we should get some very large but not too many.
+    huge = sum(x > 1e97 for x in values)
+    assert huge != 0
+    assert huge <= 0.3 * len(values)  # expected ~1/8
diff --git a/hypothesis-python/tests/numpy/test_gen_data.py b/hypothesis-python/tests/numpy/test_gen_data.py
--- a/hypothesis-python/tests/numpy/test_gen_data.py
+++ b/hypothesis-python/tests/numpy/test_gen_data.py
@@ -359,6 +359,7 @@ def test_overflowing_integers_are_deprecated(fill, data):
     "dtype,strat",
     [
         ("float16", st.floats(min_value=65520, allow_infinity=False)),
+        ("float32", st.floats(min_value=10 ** 40, allow_infinity=False)),
         ("complex64", st.complex_numbers(10 ** 300, allow_infinity=False)),
         ("U1", st.text(min_size=2, max_size=2)),
         ("S1", st.binary(min_size=2, max_size=2)),