@@ -40,7 +40,6 @@ def round_float(
40
40
# Constants
41
41
p = fi .precision
42
42
bias = fi .expBias
43
- t = p - 1
44
43
45
44
if np .isnan (v ):
46
45
if fi .num_nans == 0 :
@@ -56,59 +55,57 @@ def round_float(
56
55
if np .isinf (vpos ):
57
56
result = np .inf
58
57
59
- elif fi .has_subnormals and vpos < fi .smallest_subnormal / 2 :
60
- # Test against smallest_subnormal to avoid subnormals in frexp below
61
- # Note that this restricts us to types narrower than float64
62
- result = 0.0
58
+ elif vpos == 0 :
59
+ result = 0
63
60
64
61
else :
65
- # Extract significand (mantissa) and exponent
66
- fsignificand , expval = np .frexp (vpos )
67
- assert fsignificand >= 0.5 and fsignificand < 1.0
68
- # Bring significand into range [1.0, 2.0)
69
- fsignificand *= 2
70
- expval -= 1
62
+ # Extract exponent
63
+ expval = int (np .floor (np .log2 (vpos )))
64
+
65
+ assert expval > - 1024 + p # not yet tested for float64 near-subnormals
71
66
72
67
# Effective precision, accounting for right shift for subnormal values
73
- biased_exp = expval + bias
74
68
if fi .has_subnormals :
75
- effective_precision = t + min (biased_exp - 1 , 0 )
76
- else :
77
- effective_precision = t
69
+ expval = max (expval , 1 - bias )
78
70
79
71
# Lift to "integer * 2^e"
80
- fsignificand *= 2.0 ** effective_precision
81
- expval -= effective_precision
72
+ expval = expval - p + 1
73
+
74
+ fsignificand = vpos * 2.0 ** - expval
82
75
83
76
# Round
84
77
isignificand = math .floor (fsignificand )
85
- if isignificand != fsignificand :
86
- # Need to round
87
- if rnd == RoundMode .TowardZero :
88
- pass
89
- elif rnd == RoundMode .TowardPositive :
90
- isignificand += 1 if not sign else 0
91
- elif rnd == RoundMode .TowardNegative :
92
- isignificand += 1 if sign else 0
93
- else :
94
- # Round to nearest
95
- d = fsignificand - isignificand
96
- if d > 0.5 :
97
- isignificand += 1
98
- elif d == 0.5 :
99
- # Tie
100
- if rnd == RoundMode .TiesToAway :
101
- isignificand += 1
102
- else :
103
- # All other modes tie to even
104
- if fi .precision == 1 :
105
- # No significand bits
106
- assert (isignificand == 1 ) or (isignificand == 0 )
107
- if _isodd (biased_exp ):
108
- expval += 1
109
- else :
110
- if _isodd (isignificand ):
111
- isignificand += 1
78
+ delta = fsignificand - isignificand
79
+ if (
80
+ (rnd == RoundMode .TowardPositive and not sign and delta > 0 )
81
+ or (rnd == RoundMode .TowardNegative and sign and delta > 0 )
82
+ or (rnd == RoundMode .TiesToAway and delta >= 0.5 )
83
+ or (rnd == RoundMode .TiesToEven and delta > 0.5 )
84
+ or (rnd == RoundMode .TiesToEven and delta == 0.5 and _isodd (isignificand ))
85
+ ):
86
+ isignificand += 1
87
+
88
+ ## Special case for Precision=1, all-log format with zero.
89
+ if fi .precision == 1 :
90
+ # The logic is simply duplicated for clarity of reading.
91
+ isignificand = math .floor (fsignificand )
92
+ code_is_odd = isignificand != 0 and _isodd (expval + bias )
93
+ if (
94
+ (rnd == RoundMode .TowardPositive and not sign and delta > 0 )
95
+ or (rnd == RoundMode .TowardNegative and sign and delta > 0 )
96
+ or (rnd == RoundMode .TiesToAway and delta >= 0.5 )
97
+ or (rnd == RoundMode .TiesToEven and delta > 0.5 )
98
+ or (rnd == RoundMode .TiesToEven and delta == 0.5 and code_is_odd )
99
+ ):
100
+ # Go to nextUp.
101
+ # Increment isignificand if zero,
102
+ # else increment exponent
103
+ if isignificand == 0 :
104
+ isignificand = 1
105
+ else :
106
+ assert isignificand == 1
107
+ expval += 1
108
+ ## End special case for Precision=1.
112
109
113
110
result = isignificand * (2.0 ** expval )
114
111
@@ -119,9 +116,15 @@ def round_float(
119
116
return 0.0
120
117
121
118
# Overflow
122
- if result > (- fi .min if sign else fi .max ):
123
- if sat :
124
- result = fi .max
119
+ amax = - fi .min if sign else fi .max
120
+ if result > amax :
121
+ if (
122
+ sat
123
+ or (rnd == RoundMode .TowardNegative and not sign and np .isfinite (v ))
124
+ or (rnd == RoundMode .TowardPositive and sign and np .isfinite (v ))
125
+ or (rnd == RoundMode .TowardZero and np .isfinite (v ))
126
+ ):
127
+ result = amax
125
128
else :
126
129
if fi .has_infs :
127
130
result = np .inf
0 commit comments