Skip to content

Commit

Permalink
Generate subnormal floats
Browse files Browse the repository at this point in the history
  • Loading branch information
Zac-HD committed Nov 18, 2021
1 parent 65b4b21 commit 7411a82
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 14 deletions.
8 changes: 8 additions & 0 deletions hypothesis-python/RELEASE.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
RELEASE_TYPE: patch

This patch makes :func:`hypothesis.strategies.floats` generate
:wikipedia:`"subnormal" floating point numbers <Subnormal_number>`
more often, as these rare values can have strange interactions with
`unsafe compiler optimisations like -ffast-math
<https://simonbyrne.github.io/notes/fastmath/#flushing_subnormals_to_zero>`__
(:issue:`2976`).
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import operator
from decimal import Decimal
from fractions import Fraction
from sys import float_info
from typing import Optional, Union

from hypothesis.control import assume, reject
Expand Down Expand Up @@ -157,15 +158,18 @@ def integers(
10e6,
10e-6,
1.175494351e-38,
2.2250738585072014e-308,
1.7976931348623157e308,
next_up(0.0),
float_info.min,
float_info.max,
3.402823466e38,
9007199254740992,
1 - 10e-6,
2 + 10e-6,
1.192092896e-07,
2.2204460492503131e-016,
]
+ [2.0 ** -n for n in (24, 14, 149, 126)] # minimum (sub)normals for float16,32
+ [float_info.min / n for n in (2, 10, 1000, 100_000)] # subnormal in float64
+ [math.inf, math.nan] * 5,
key=flt.float_to_lex,
)
Expand Down
24 changes: 12 additions & 12 deletions hypothesis-python/tests/nocover/test_floating.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import pytest

from hypothesis import HealthCheck, assume, given, settings
from hypothesis.internal.floats import next_down
from hypothesis.strategies import data, floats, lists

from tests.common.utils import fails
Expand Down Expand Up @@ -105,35 +106,34 @@ def test_is_in_exact_int_range(x):


# Tests whether we can represent subnormal floating point numbers.
# This is essentially a function of how the python interpreter
# was compiled.
# IEE-754 requires subnormal support, but it's often disabled anyway by unsafe
# compiler options like `-ffast-math`. On most hardware that's even a global
# config option, so *linking against* something built this way can break us.
# Everything is terrible
if math.ldexp(0.25, -1022) > 0:
REALLY_SMALL_FLOAT = sys.float_info.min
else:
REALLY_SMALL_FLOAT = sys.float_info.min * 2
FLUSH_SUBNORMALS_TO_ZERO = next_down(sys.float_info.min) == 0.0


# These two tests have been failing for an unknown amount of time, but that
# failure was previously being masked by a bug in our `@fails` decorator.
def test_compiled_with_sane_math_options():
# Checks that we're not unexpectedly skipping the subnormal tests below.
assert not FLUSH_SUBNORMALS_TO_ZERO


@pytest.mark.xfail
@pytest.mark.skipif(FLUSH_SUBNORMALS_TO_ZERO, reason="broken by unsafe compiler flags")
@fails
@given(floats())
@TRY_HARDER
def test_can_generate_really_small_positive_floats(x):
assume(x > 0)
assert x >= REALLY_SMALL_FLOAT
assert x >= sys.float_info.min


@pytest.mark.xfail
@pytest.mark.skipif(FLUSH_SUBNORMALS_TO_ZERO, reason="broken by unsafe compiler flags")
@fails
@given(floats())
@TRY_HARDER
def test_can_generate_really_small_negative_floats(x):
assume(x < 0)
assert x <= -REALLY_SMALL_FLOAT
assert x <= -sys.float_info.min


@fails
Expand Down

0 comments on commit 7411a82

Please sign in to comment.