1 # SPDX-License-Identifier: LGPLv3+
2 # Funded by NLnet https://nlnet.nl/
4 """ implementation of binary floating-point working format as used in:
5 PowerISA v3.1B section 7.6.2.2
6 e.g. bfp_CONVERT_FROM_BFP32() on page 589(615)
9 from openpower
.decoder
.selectable_int
import SelectableInt
12 from fractions
import Fraction
14 # in this file, everything uses properties instead of plain attributes because
15 # we need to convert most SelectableInts we get to Python int
19 def __init__(self
, value
=None):
32 self
.Infinity
= rhs
.Infinity
34 self
.Denormal
= rhs
.Denormal
35 self
.Normal
= rhs
.Normal
42 def SNaN(self
, value
):
43 self
.__snan
= int(value
)
50 def QNaN(self
, value
):
51 self
.__qnan
= int(value
)
55 return self
.__infinity
58 def Infinity(self
, value
):
59 self
.__infinity
= int(value
)
66 def Zero(self
, value
):
67 self
.__zero
= int(value
)
71 return self
.__denormal
74 def Denormal(self
, value
):
75 self
.__denormal
= int(value
)
82 def Normal(self
, value
):
83 self
.__normal
= int(value
)
85 def __eq__(self
, other
):
86 if isinstance(other
, BFPStateClass
):
87 return (self
.SNaN
== other
.SNaN
and
88 self
.QNaN
== other
.QNaN
and
89 self
.Infinity
== other
.Infinity
and
90 self
.Zero
== other
.Zero
and
91 self
.Denormal
== other
.Denormal
and
92 self
.Normal
== other
.Normal
)
95 def _bfp_state_fields(self
):
96 return (f
"class_.SNaN: {self.SNaN}",
97 f
"class_.QNaN: {self.QNaN}",
98 f
"class_.Infinity: {self.Infinity}",
99 f
"class_.Zero: {self.Zero}",
100 f
"class_.Denormal: {self.Denormal}",
101 f
"class_.Normal: {self.Normal}")
104 fields
= self
._bfp
_state
_fields
()
105 return f
"<BFPStateClass {fields}>"
108 class SelectableMSB0Fraction
:
109 """a MSB0 infinite bit string that is a real number generally between 0
110 and 2, but we approximate it using a Fraction.
112 bit 0 is the lsb of the integer part,
113 bit 1 is the msb of the fraction part,
114 bit 2 is the next-to-msb of the fraction part, etc.
116 this is not SelectableInt because we need more bits and because this isn't
117 an integer -- it can represent unlimited values, both really small and
118 really large -- corresponding to having bits be able to be set in bit
119 indexes 0, -1, -2, -3, etc.
122 def __init__(self
, value
=None):
123 self
.__value
= Fraction()
124 if value
is not None:
133 self
.__value
= Fraction(v
)
136 def __get_slice_dimensions(index
):
137 if isinstance(index
, slice):
138 if index
.stop
is None or index
.step
is not None:
139 raise ValueError("unsupported slice kind")
140 # use int() to convert from
141 start
= int(0 if index
.start
is None else index
.start
)
142 stop
= int(index
.stop
)
143 # pseudo-code compiler converts from inclusive to
144 # standard Python slices
145 length
= stop
- start
151 def __slice_as_int(self
, start
, length
):
152 if start
< 0 or length
< 0:
153 raise ValueError("slice out of range")
156 last
= start
+ length
- 1
157 # shift so bits we want are the lsb bits of the integer part
158 v
= math
.floor(self
.value
* (1 << last
))
159 return v
& ~
(~
0 << length
) # mask off unwanted bits
161 def __set_slice(self
, start
, length
, value
):
162 if start
< 0 or length
< 0:
163 raise ValueError("slice out of range")
166 last
= start
+ length
- 1
167 shift_factor
= 1 << last
168 # shift so bits we want to replace are the lsb bits of the integer part
169 v
= self
.value
* shift_factor
170 mask
= ~
(~
0 << length
)
171 # convert any SelectableInts to int and mask
172 value
= int(value
) & mask
173 # compute how much we need to add
174 offset
= value
- (math
.floor(v
) & mask
)
175 # shift offset back into position
176 offset
= Fraction(offset
, shift_factor
)
179 def __getitem__(self
, index
):
180 start
, length
= self
.__get
_slice
_dimensions
(index
)
181 return SelectableInt(self
.__slice
_as
_int
(start
, length
), length
)
183 def __setitem__(self
, index
, value
):
184 start
, length
= self
.__get
_slice
_dimensions
(index
)
185 self
.__set
_slice
(start
, length
, value
)
188 max_int_digits
=4, # don't need much since this is generally
189 # supposed to be in [0, 1]
190 max_fraction_digits
=17, # u64 plus 1
191 fraction_sep_period
=4, # how many fraction digits between `_`s
193 """ convert to a string of the form: `0x3a.bc` or
194 `0x...face.face_face_face_face... (0xa8ef0000 / 0x5555)`"""
195 if max_int_digits
<= 0 or max_fraction_digits
<= 0:
196 raise ValueError("invalid digit limit")
198 int_part
= math
.floor(self
.value
)
199 int_part_limit
= 0x10 ** max_int_digits
200 if 0 <= int_part
< int_part_limit
:
201 int_str
= hex(int_part
)
204 int_part
%= int_part_limit
205 int_str
= f
"0x...{int_part:0{max_int_digits}x}"
207 factor
= 0x10 ** max_fraction_digits
208 fraction_part_exact
= (self
.value
- math
.floor(self
.value
)) * factor
209 fraction_part
= math
.floor(fraction_part_exact
)
211 if fraction_part
== fraction_part_exact
:
212 # extract least-significant set bit of fraction_part
213 fraction_part_lsb
= fraction_part
& -fraction_part
214 log2_fraction_part_lsb
= fraction_part_lsb
.bit_length() - 1
215 zero_fraction_digits
= max(0, log2_fraction_part_lsb
// 4)
216 fraction_part
>>= 4 * zero_fraction_digits
217 fraction_digits
= max_fraction_digits
- zero_fraction_digits
222 fraction_digits
= max_fraction_digits
223 fraction_str
= f
"{fraction_part:0{fraction_digits}x}"
225 if fraction_sep_period
is not None and fraction_sep_period
> 0:
226 for i
in range(0, len(fraction_str
), fraction_sep_period
):
227 fraction_parts
.append(fraction_str
[i
:i
+ fraction_sep_period
])
228 fraction_str
= "_".join(fraction_parts
)
229 fraction_str
= "." + fraction_str
+ suffix
231 if self
.value
.denominator
!= 1:
232 retval
+= fraction_str
236 n
= self
.value
.numerator
237 d
= self
.value
.denominator
238 fraction
= f
" ({n:#x} / {d:#x})"
240 # is the denominator a power of 2?
241 if (self
.value
.denominator
& (self
.value
.denominator
- 1)) == 0:
242 log2_d
= self
.value
.denominator
.bit_length() - 1
244 if self
.value
.denominator
== 1:
245 fraction
= f
" ({n:#x})"
247 # extract least-significant set bit of n
250 log2_n_lsb
= n_lsb
.bit_length() - 1
251 exponent
= log2_n_lsb
- log2_d
252 if exponent
< -8 or exponent
> 8:
253 fraction
= f
" ({n:#x} * 2**{exponent})"
258 return "SelectableMSB0Fraction(" + str(self
) + ")"
261 if isinstance(value
, (int, Fraction
)):
262 self
.value
= Fraction(value
)
263 elif isinstance(value
, SelectableMSB0Fraction
):
264 self
.value
= value
.value
266 raise ValueError("unsupported assignment type")
269 return self
.value
!= 0
272 return SelectableMSB0Fraction(-self
.value
)
275 return SelectableMSB0Fraction(self
)
278 return SelectableMSB0Fraction(math
.floor(self
.value
))
281 return SelectableMSB0Fraction(math
.ceil(self
.value
))
284 return SelectableMSB0Fraction(math
.trunc(self
.value
))
287 return SelectableMSB0Fraction(round(self
.value
))
290 def __arith_op(lhs
, rhs
, op
):
291 lhs
= SelectableMSB0Fraction(lhs
)
292 rhs
= SelectableMSB0Fraction(rhs
)
293 return SelectableMSB0Fraction(op(lhs
.value
, rhs
.value
))
295 def __add__(self
, other
):
296 return self
.__arith
_op
(self
, other
, operator
.add
)
300 def __mul__(self
, other
):
301 return self
.__arith
_op
(self
, other
, operator
.mul
)
305 def __sub__(self
, other
):
306 return self
.__arith
_op
(self
, other
, operator
.sub
)
308 def __rsub__(self
, other
):
309 return self
.__arith
_op
(other
, self
, operator
.sub
)
311 def __truediv__(self
, other
):
312 return self
.__arith
_op
(self
, other
, operator
.truediv
)
314 def __rtruediv__(self
, other
):
315 return self
.__arith
_op
(other
, self
, operator
.truediv
)
317 def __floordiv__(self
, other
):
318 return self
.__arith
_op
(self
, other
, operator
.floordiv
)
320 def __rfloordiv__(self
, other
):
321 return self
.__arith
_op
(other
, self
, operator
.floordiv
)
323 def __mod__(self
, other
):
324 return self
.__arith
_op
(self
, other
, operator
.mod
)
326 def __rmod__(self
, other
):
327 return self
.__arith
_op
(other
, self
, operator
.mod
)
329 def __lshift__(self
, amount
):
330 if not isinstance(amount
, int):
331 raise TypeError("can't shift by non-int")
333 return SelectableMSB0Fraction(self
.value
/ (1 << -amount
))
334 return SelectableMSB0Fraction(self
.value
* (1 << amount
))
336 def __rlshift__(self
, other
):
337 raise TypeError("can't shift by non-int")
339 def __rshift__(self
, amount
):
340 if not isinstance(amount
, int):
341 raise TypeError("can't shift by non-int")
342 return self
<< -amount
344 def __rrshift__(self
, other
):
345 raise TypeError("can't shift by non-int")
347 def __cmp_op(self
, other
, op
):
348 if isinstance(other
, (int, Fraction
)):
350 elif isinstance(other
, SelectableMSB0Fraction
):
353 return NotImplemented
354 return op(self
.value
, other
)
356 def __eq__(self
, other
):
357 return self
.__cmp
_op
(other
, operator
.eq
)
359 def __ne__(self
, other
):
360 return self
.__cmp
_op
(other
, operator
.ne
)
362 def __lt__(self
, other
):
363 return self
.__cmp
_op
(other
, operator
.lt
)
365 def __le__(self
, other
):
366 return self
.__cmp
_op
(other
, operator
.le
)
368 def __gt__(self
, other
):
369 return self
.__cmp
_op
(other
, operator
.gt
)
371 def __ge__(self
, other
):
372 return self
.__cmp
_op
(other
, operator
.ge
)
376 """ implementation of binary floating-point working format as used in:
377 PowerISA v3.1B section 7.6.2.2
378 e.g. bfp_CONVERT_FROM_BFP32() on page 589(615)
381 def __init__(self
, value
=None):
384 self
.__significand
= SelectableMSB0Fraction()
385 self
.__class
= BFPStateClass()
386 if value
is not None:
391 self
.exponent
= rhs
.exponent
392 self
.significand
= rhs
.significand
393 self
.class_
= rhs
.class_
400 def sign(self
, value
):
401 self
.__sign
= int(value
)
405 return self
.__exponent
408 def exponent(self
, value
):
409 self
.__exponent
= int(value
)
412 def significand(self
):
413 return self
.__significand
416 def significand(self
, value
):
417 self
.__significand
.eq(value
)
424 def class_(self
, value
):
425 self
.__class
.eq(value
)
427 def __eq__(self
, other
):
428 if isinstance(other
, BFPStateClass
):
429 return self
._bfp
_state
_fields
() == other
._bfp
_state
_fields
()
430 return NotImplemented
432 def _bfp_state_fields(self
):
433 class_fields
= self
.class_
._bfp
_state
_fields
()
434 return (f
"sign: {self.sign}",
435 f
"exponent: {self.exponent}",
436 f
"significand: {self.significand}",
437 *self
.class_
._bfp
_state
_fields
())
440 fields
= self
._bfp
_state
_fields
()
441 return f
"<BFPState {fields}>"