Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/hypothesis/internal/floats.py: 83%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

42 statements  

1# This file is part of Hypothesis, which may be found at 

2# https://github.com/HypothesisWorks/hypothesis/ 

3# 

4# Copyright the Hypothesis Authors. 

5# Individual contributors are listed in AUTHORS.rst and the git log. 

6# 

7# This Source Code Form is subject to the terms of the Mozilla Public License, 

8# v. 2.0. If a copy of the MPL was not distributed with this file, You can 

9# obtain one at https://mozilla.org/MPL/2.0/. 

10 

11import math 

12from collections.abc import Callable 

13from sys import float_info 

14from typing import overload 

15 

16from hypothesis._native.internal.floats import ( 

17 count_between_floats as count_between_floats, 

18 float_of as float_of, 

19 float_to_int as float_to_int, 

20 int_to_float as int_to_float, 

21 is_negative as is_negative, 

22 next_down as next_down, 

23 next_down_normal as next_down_normal, 

24 next_up as next_up, 

25 next_up_normal as next_up_normal, 

26 width_smallest_normals as width_smallest_normals, 

27) 

28 

29assert width_smallest_normals(64) == float_info.min 

30 

31mantissa_mask = (1 << 52) - 1 

32 

33 

34def make_float_clamper( 

35 min_value: float, 

36 max_value: float, 

37 *, 

38 allow_nan: bool, 

39 smallest_nonzero_magnitude: float, 

40) -> Callable[[float], float]: 

41 """ 

42 Return a function that clamps positive floats into the given bounds. 

43 """ 

44 from hypothesis.internal.conjecture.choice import choice_permitted 

45 

46 assert sign_aware_lte(min_value, max_value) 

47 range_size = min(max_value - min_value, float_info.max) 

48 

49 def float_clamper(f: float) -> float: 

50 if choice_permitted( 

51 f, 

52 { 

53 "min_value": min_value, 

54 "max_value": max_value, 

55 "allow_nan": allow_nan, 

56 "smallest_nonzero_magnitude": smallest_nonzero_magnitude, 

57 }, 

58 ): 

59 return f 

60 # Outside bounds; pick a new value, sampled from the allowed range, 

61 # using the mantissa bits. 

62 mant = float_to_int(abs(f)) & mantissa_mask 

63 f = min_value + range_size * (mant / mantissa_mask) 

64 

65 # if we resampled into the space disallowed by smallest_nonzero_magnitude, 

66 # default to smallest_nonzero_magnitude. 

67 if 0 < abs(f) < smallest_nonzero_magnitude: 

68 f = smallest_nonzero_magnitude 

69 # we must have either -smallest_nonzero_magnitude <= min_value or 

70 # smallest_nonzero_magnitude >= max_value, or no values would be 

71 # possible. If smallest_nonzero_magnitude is not valid (because it's 

72 # larger than max_value), then -smallest_nonzero_magnitude must be valid. 

73 if smallest_nonzero_magnitude > max_value: 

74 f *= -1 

75 

76 # Re-enforce the bounds (just in case of floating point arithmetic error) 

77 return clamp(min_value, f, max_value) 

78 

79 return float_clamper 

80 

81 

82def sign_aware_lte(x: float | int, y: float | int) -> bool: 

83 """Less-than-or-equals, but strictly orders -0.0 and 0.0""" 

84 if x == 0.0 == y: 

85 return math.copysign(1.0, x) <= math.copysign(1.0, y) 

86 else: 

87 return x <= y 

88 

89 

90@overload 

91def clamp(lower: int, value: int, upper: int) -> int: ... 

92@overload 

93def clamp(lower: float, value: float, upper: float) -> float: ... 

94def clamp(lower: float | int, value: float | int, upper: float | int) -> float | int: 

95 """Given a value and lower/upper bounds, 'clamp' the value so that 

96 it satisfies lower <= value <= upper. NaN is mapped to lower.""" 

97 # this seems pointless (and is for integers), but handles the -0.0/0.0 case. 

98 if not sign_aware_lte(lower, value): 

99 return lower 

100 if not sign_aware_lte(value, upper): 

101 return upper 

102 return value 

103 

104 

105SMALLEST_SUBNORMAL = next_up(0.0) 

106SIGNALING_NAN = int_to_float(0x7FF8_0000_0000_0001) # nonzero mantissa 

107MAX_PRECISE_INTEGER = 2**53 

108assert math.isnan(SIGNALING_NAN) 

109assert math.copysign(1, SIGNALING_NAN) == 1