flexfloat 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flexfloat/__init__.py +21 -17
- flexfloat/bitarray/__init__.py +21 -80
- flexfloat/bitarray/bitarray.py +154 -54
- flexfloat/bitarray/bitarray_bigint.py +305 -0
- flexfloat/bitarray/bitarray_bool.py +202 -0
- flexfloat/bitarray/bitarray_int64.py +174 -149
- flexfloat/bitarray/bitarray_mixins.py +110 -47
- flexfloat/core.py +215 -113
- flexfloat/types.py +7 -1
- {flexfloat-0.1.3.dist-info → flexfloat-0.1.5.dist-info}/METADATA +1 -1
- flexfloat-0.1.5.dist-info/RECORD +15 -0
- flexfloat/bitarray/bitarray_list.py +0 -187
- flexfloat-0.1.3.dist-info/RECORD +0 -14
- {flexfloat-0.1.3.dist-info → flexfloat-0.1.5.dist-info}/WHEEL +0 -0
- {flexfloat-0.1.3.dist-info → flexfloat-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {flexfloat-0.1.3.dist-info → flexfloat-0.1.5.dist-info}/top_level.txt +0 -0
flexfloat/core.py
CHANGED
@@ -1,11 +1,25 @@
|
|
1
|
-
"""Core FlexFloat class implementation.
|
1
|
+
"""Core FlexFloat class implementation.
|
2
|
+
|
3
|
+
This module defines the FlexFloat class, which represents a floating-point number with a
|
4
|
+
growable exponent and a fixed-size fraction. The class is designed for arbitrary
|
5
|
+
precision floating-point arithmetic, supporting very large or small values by
|
6
|
+
dynamically adjusting the exponent size.
|
7
|
+
|
8
|
+
Example:
|
9
|
+
from flexfloat import FlexFloat
|
10
|
+
a = FlexFloat(3.14, exponent_length=10, fraction_length=20)
|
11
|
+
b = FlexFloat(2.71, exponent_length=10, fraction_length=20)
|
12
|
+
c = a + b
|
13
|
+
print(c)
|
14
|
+
# Output: FlexFloat(...)
|
15
|
+
"""
|
2
16
|
|
3
17
|
from __future__ import annotations
|
4
18
|
|
5
19
|
import math
|
6
|
-
from typing import ClassVar, Final
|
20
|
+
from typing import ClassVar, Final, Type
|
7
21
|
|
8
|
-
from .bitarray import BitArray,
|
22
|
+
from .bitarray import BitArray, ListBoolBitArray
|
9
23
|
from .types import Number
|
10
24
|
|
11
25
|
LOG10_2: Final[float] = math.log10(2)
|
@@ -29,6 +43,26 @@ class FlexFloat:
|
|
29
43
|
"""
|
30
44
|
|
31
45
|
e: ClassVar[FlexFloat]
|
46
|
+
"""The mathematical constant e as a FlexFloat instance."""
|
47
|
+
_bitarray_implementation: ClassVar[Type[BitArray]] = ListBoolBitArray
|
48
|
+
"""The BitArray implementation class used for all FlexFloat instances."""
|
49
|
+
|
50
|
+
sign: bool
|
51
|
+
"""The sign of the number (True for negative, False for positive)."""
|
52
|
+
exponent: BitArray
|
53
|
+
"""A growable bit array representing the exponent (uses off-set binary
|
54
|
+
representation)."""
|
55
|
+
fraction: BitArray
|
56
|
+
"""A fixed-size bit array representing the fraction (mantissa) of the number."""
|
57
|
+
|
58
|
+
@classmethod
|
59
|
+
def set_bitarray_implementation(cls, implementation: Type[BitArray]) -> None:
|
60
|
+
"""Set the BitArray implementation to use for all FlexFloat instances.
|
61
|
+
|
62
|
+
Args:
|
63
|
+
implementation (Type[BitArray]): The BitArray implementation class to use.
|
64
|
+
"""
|
65
|
+
cls._bitarray_implementation = implementation
|
32
66
|
|
33
67
|
def __init__(
|
34
68
|
self,
|
@@ -36,49 +70,70 @@ class FlexFloat:
|
|
36
70
|
exponent: BitArray | None = None,
|
37
71
|
fraction: BitArray | None = None,
|
38
72
|
):
|
39
|
-
"""
|
73
|
+
"""Initializes a FlexFloat instance.
|
74
|
+
|
75
|
+
BitArrays are expected to be LSB-first (least significant bit at index 0,
|
76
|
+
increasing to MSB).
|
40
77
|
|
41
78
|
Args:
|
42
|
-
sign (bool): The sign of the number (True for negative, False for
|
43
|
-
|
44
|
-
|
79
|
+
sign (bool, optional): The sign of the number (True for negative, False for
|
80
|
+
positive). Defaults to False.
|
81
|
+
exponent (BitArray | None, optional): The exponent bit array. If None,
|
82
|
+
represents 0. Defaults to None.
|
83
|
+
fraction (BitArray | None, optional): The fraction bit array. If None,
|
84
|
+
represents 0. Defaults to None.
|
45
85
|
"""
|
46
86
|
self.sign = sign
|
47
|
-
self.exponent =
|
48
|
-
|
87
|
+
self.exponent = (
|
88
|
+
exponent
|
89
|
+
if exponent is not None
|
90
|
+
else self._bitarray_implementation.zeros(11)
|
91
|
+
)
|
92
|
+
self.fraction = (
|
93
|
+
fraction
|
94
|
+
if fraction is not None
|
95
|
+
else self._bitarray_implementation.zeros(52)
|
96
|
+
)
|
49
97
|
|
50
98
|
@classmethod
|
51
99
|
def from_float(cls, value: Number) -> FlexFloat:
|
52
|
-
"""
|
100
|
+
"""Creates a FlexFloat instance from a number.
|
53
101
|
|
54
102
|
Args:
|
55
103
|
value (Number): The number to convert to FlexFloat.
|
104
|
+
|
56
105
|
Returns:
|
57
106
|
FlexFloat: A new FlexFloat instance representing the number.
|
58
107
|
"""
|
59
108
|
value = float(value)
|
60
|
-
bits =
|
109
|
+
bits = cls._bitarray_implementation.from_float(value)
|
61
110
|
|
62
|
-
return cls(sign=bits[
|
111
|
+
return cls(sign=bits[63], exponent=bits[52:63], fraction=bits[:52])
|
63
112
|
|
64
113
|
def to_float(self) -> float:
|
65
|
-
"""
|
114
|
+
"""Converts the FlexFloat instance back to a 64-bit float.
|
66
115
|
|
67
116
|
If float is bigger than 64 bits, it will truncate the value to fit.
|
68
117
|
|
69
118
|
Returns:
|
70
119
|
float: The floating-point number represented by the FlexFloat instance.
|
120
|
+
|
71
121
|
Raises:
|
72
|
-
ValueError: If the
|
122
|
+
ValueError: If the FlexFloat does not have standard 64-bit exponent and
|
123
|
+
fraction.
|
73
124
|
"""
|
74
125
|
if len(self.exponent) < 11 or len(self.fraction) < 52:
|
75
126
|
raise ValueError("Must be a standard 64-bit FlexFloat")
|
76
127
|
|
77
|
-
bits =
|
128
|
+
bits = (
|
129
|
+
self.fraction[:52]
|
130
|
+
+ self.exponent[:11]
|
131
|
+
+ self._bitarray_implementation.from_bits([self.sign])
|
132
|
+
)
|
78
133
|
return bits.to_float()
|
79
134
|
|
80
135
|
def __repr__(self) -> str:
|
81
|
-
"""
|
136
|
+
"""Returns a string representation of the FlexFloat instance.
|
82
137
|
|
83
138
|
Returns:
|
84
139
|
str: A string representation of the FlexFloat instance.
|
@@ -91,7 +146,7 @@ class FlexFloat:
|
|
91
146
|
)
|
92
147
|
|
93
148
|
def pretty(self) -> str:
|
94
|
-
"""
|
149
|
+
"""Returns an easier to read string representation of the FlexFloat instance.
|
95
150
|
Mainly converts the exponent and fraction to integers for readability.
|
96
151
|
|
97
152
|
Returns:
|
@@ -104,54 +159,52 @@ class FlexFloat:
|
|
104
159
|
|
105
160
|
@classmethod
|
106
161
|
def nan(cls) -> FlexFloat:
|
107
|
-
"""
|
162
|
+
"""Creates a FlexFloat instance representing NaN (Not a Number).
|
108
163
|
|
109
164
|
Returns:
|
110
165
|
FlexFloat: A new FlexFloat instance representing NaN.
|
111
166
|
"""
|
112
|
-
exponent =
|
113
|
-
fraction =
|
167
|
+
exponent = cls._bitarray_implementation.ones(11)
|
168
|
+
fraction = cls._bitarray_implementation.ones(52)
|
114
169
|
return cls(sign=True, exponent=exponent, fraction=fraction)
|
115
170
|
|
116
171
|
@classmethod
|
117
172
|
def infinity(cls, sign: bool = False) -> FlexFloat:
|
118
|
-
"""
|
173
|
+
"""Creates a FlexFloat instance representing Infinity.
|
119
174
|
|
120
175
|
Args:
|
121
|
-
sign (bool): Indicates if the infinity is negative.
|
176
|
+
sign (bool, optional): Indicates if the infinity is negative. Defaults to
|
177
|
+
False.
|
178
|
+
|
122
179
|
Returns:
|
123
180
|
FlexFloat: A new FlexFloat instance representing Infinity.
|
124
181
|
"""
|
125
|
-
exponent =
|
126
|
-
fraction =
|
182
|
+
exponent = cls._bitarray_implementation.ones(11)
|
183
|
+
fraction = cls._bitarray_implementation.zeros(52)
|
127
184
|
return cls(sign=sign, exponent=exponent, fraction=fraction)
|
128
185
|
|
129
186
|
@classmethod
|
130
187
|
def zero(cls) -> FlexFloat:
|
131
|
-
"""
|
188
|
+
"""Creates a FlexFloat instance representing zero.
|
132
189
|
|
133
190
|
Returns:
|
134
191
|
FlexFloat: A new FlexFloat instance representing zero.
|
135
192
|
"""
|
136
|
-
exponent =
|
137
|
-
fraction =
|
193
|
+
exponent = cls._bitarray_implementation.zeros(11)
|
194
|
+
fraction = cls._bitarray_implementation.zeros(52)
|
138
195
|
return cls(sign=False, exponent=exponent, fraction=fraction)
|
139
196
|
|
140
197
|
def _is_special_exponent(self) -> bool:
|
141
|
-
"""
|
198
|
+
"""Checks if the exponent represents a special value (NaN or Infinity).
|
142
199
|
|
143
200
|
Returns:
|
144
201
|
bool: True if the exponent is at its maximum value, False otherwise.
|
145
202
|
"""
|
146
|
-
# In IEEE 754, special values have all exponent bits set to 1
|
147
|
-
# This corresponds to the maximum value in the unsigned representation
|
148
|
-
# For signed offset binary, the maximum value is 2^(n-1) - 1
|
149
|
-
# where n is the number of bits
|
150
203
|
max_signed_value = (1 << (len(self.exponent) - 1)) - 1
|
151
204
|
return self.exponent.to_signed_int() == max_signed_value
|
152
205
|
|
153
206
|
def is_nan(self) -> bool:
|
154
|
-
"""
|
207
|
+
"""Checks if the FlexFloat instance represents NaN (Not a Number).
|
155
208
|
|
156
209
|
Returns:
|
157
210
|
bool: True if the FlexFloat instance is NaN, False otherwise.
|
@@ -159,7 +212,7 @@ class FlexFloat:
|
|
159
212
|
return self._is_special_exponent() and any(self.fraction)
|
160
213
|
|
161
214
|
def is_infinity(self) -> bool:
|
162
|
-
"""
|
215
|
+
"""Checks if the FlexFloat instance represents Infinity.
|
163
216
|
|
164
217
|
Returns:
|
165
218
|
bool: True if the FlexFloat instance is Infinity, False otherwise.
|
@@ -167,7 +220,7 @@ class FlexFloat:
|
|
167
220
|
return self._is_special_exponent() and not any(self.fraction)
|
168
221
|
|
169
222
|
def is_zero(self) -> bool:
|
170
|
-
"""
|
223
|
+
"""Checks if the FlexFloat instance represents zero.
|
171
224
|
|
172
225
|
Returns:
|
173
226
|
bool: True if the FlexFloat instance is zero, False otherwise.
|
@@ -175,7 +228,7 @@ class FlexFloat:
|
|
175
228
|
return not any(self.exponent) and not any(self.fraction)
|
176
229
|
|
177
230
|
def copy(self) -> FlexFloat:
|
178
|
-
"""
|
231
|
+
"""Creates a copy of the FlexFloat instance.
|
179
232
|
|
180
233
|
Returns:
|
181
234
|
FlexFloat: A new FlexFloat instance with the same data as the original.
|
@@ -185,12 +238,13 @@ class FlexFloat:
|
|
185
238
|
)
|
186
239
|
|
187
240
|
def __str__(self) -> str:
|
188
|
-
"""
|
241
|
+
"""Returns a float representation of the FlexFloat using a generic algorithm.
|
189
242
|
|
190
|
-
|
191
|
-
|
243
|
+
Currently, it only operates in one format: scientific notation with 5 decimal
|
244
|
+
places.
|
192
245
|
|
193
|
-
|
246
|
+
Returns:
|
247
|
+
str: The string representation in scientific notation.
|
194
248
|
"""
|
195
249
|
sign_str = "-" if self.sign else ""
|
196
250
|
# Handle special cases first
|
@@ -208,7 +262,7 @@ class FlexFloat:
|
|
208
262
|
# Convert fraction to decimal value between 1 and 2
|
209
263
|
# (starting with 1.0 for the implicit leading bit)
|
210
264
|
mantissa = 1.0
|
211
|
-
for i, bit in enumerate(self.fraction):
|
265
|
+
for i, bit in enumerate(reversed(self.fraction)):
|
212
266
|
if bit:
|
213
267
|
mantissa += 1.0 / (1 << (i + 1))
|
214
268
|
|
@@ -235,7 +289,11 @@ class FlexFloat:
|
|
235
289
|
return f"{sign_str}{normalized_mantissa:.5f}e{decimal_exponent:+03d}"
|
236
290
|
|
237
291
|
def __neg__(self) -> FlexFloat:
|
238
|
-
"""
|
292
|
+
"""Negates the FlexFloat instance.
|
293
|
+
|
294
|
+
Returns:
|
295
|
+
FlexFloat: A new FlexFloat instance with the sign flipped.
|
296
|
+
"""
|
239
297
|
return FlexFloat(
|
240
298
|
sign=not self.sign,
|
241
299
|
exponent=self.exponent.copy(),
|
@@ -244,11 +302,12 @@ class FlexFloat:
|
|
244
302
|
|
245
303
|
@staticmethod
|
246
304
|
def _grow_exponent(exponent: int, exponent_length: int) -> int:
|
247
|
-
"""
|
305
|
+
"""Grows the exponent if it exceeds the maximum value for the current length.
|
248
306
|
|
249
307
|
Args:
|
250
308
|
exponent (int): The current exponent value.
|
251
309
|
exponent_length (int): The current length of the exponent in bits.
|
310
|
+
|
252
311
|
Returns:
|
253
312
|
int: The new exponent length if it needs to be grown, otherwise the same
|
254
313
|
length.
|
@@ -265,12 +324,16 @@ class FlexFloat:
|
|
265
324
|
return exponent_length
|
266
325
|
|
267
326
|
def __add__(self, other: FlexFloat | Number) -> FlexFloat:
|
268
|
-
"""
|
327
|
+
"""Adds two FlexFloat instances together.
|
269
328
|
|
270
329
|
Args:
|
271
|
-
other (FlexFloat |
|
330
|
+
other (FlexFloat | Number): The other FlexFloat instance to add.
|
331
|
+
|
272
332
|
Returns:
|
273
333
|
FlexFloat: A new FlexFloat instance representing the sum.
|
334
|
+
|
335
|
+
Raises:
|
336
|
+
TypeError: If other is not a FlexFloat or numeric type.
|
274
337
|
"""
|
275
338
|
if isinstance(other, Number):
|
276
339
|
other = FlexFloat.from_float(other)
|
@@ -312,9 +375,9 @@ class FlexFloat:
|
|
312
375
|
exponent_self = self.exponent.to_signed_int() + 1
|
313
376
|
exponent_other = other.exponent.to_signed_int() + 1
|
314
377
|
|
315
|
-
# Step 2:
|
316
|
-
mantissa_self = [True]
|
317
|
-
mantissa_other = [True]
|
378
|
+
# Step 2: Append the implicit leading 1 to form the mantissa
|
379
|
+
mantissa_self = self.fraction + [True]
|
380
|
+
mantissa_other = other.fraction + [True]
|
318
381
|
|
319
382
|
# Step 3: Compare exponents (self is always larger or equal)
|
320
383
|
if exponent_self < exponent_other:
|
@@ -335,9 +398,10 @@ class FlexFloat:
|
|
335
398
|
f"got {len(mantissa_other)} bits."
|
336
399
|
)
|
337
400
|
|
338
|
-
|
401
|
+
# 1 leading bit + 52 fraction bits
|
402
|
+
mantissa_result = self._bitarray_implementation.zeros(53)
|
339
403
|
carry = False
|
340
|
-
for i in range(
|
404
|
+
for i in range(53):
|
341
405
|
total = mantissa_self[i] + mantissa_other[i] + carry
|
342
406
|
mantissa_result[i] = total % 2 == 1
|
343
407
|
carry = total > 1
|
@@ -355,22 +419,26 @@ class FlexFloat:
|
|
355
419
|
exponent_self - (1 << (exp_result_length - 1)) < 2
|
356
420
|
), "Exponent growth should not exceed 1 bit."
|
357
421
|
|
358
|
-
exponent_result =
|
422
|
+
exponent_result = self._bitarray_implementation.from_signed_int(
|
359
423
|
exponent_self - 1, exp_result_length
|
360
424
|
)
|
361
425
|
return FlexFloat(
|
362
426
|
sign=self.sign,
|
363
427
|
exponent=exponent_result,
|
364
|
-
fraction=mantissa_result[1
|
428
|
+
fraction=mantissa_result[:-1], # Exclude leading bit
|
365
429
|
)
|
366
430
|
|
367
431
|
def __sub__(self, other: FlexFloat | Number) -> FlexFloat:
|
368
|
-
"""
|
432
|
+
"""Subtracts one FlexFloat instance from another.
|
369
433
|
|
370
434
|
Args:
|
371
|
-
other (FlexFloat |
|
435
|
+
other (FlexFloat | Number): The FlexFloat instance to subtract.
|
436
|
+
|
372
437
|
Returns:
|
373
438
|
FlexFloat: A new FlexFloat instance representing the difference.
|
439
|
+
|
440
|
+
Raises:
|
441
|
+
TypeError: If other is not a FlexFloat or numeric type.
|
374
442
|
"""
|
375
443
|
if isinstance(other, Number):
|
376
444
|
other = FlexFloat.from_float(other)
|
@@ -417,9 +485,9 @@ class FlexFloat:
|
|
417
485
|
exponent_self = self.exponent.to_signed_int() + 1
|
418
486
|
exponent_other = other.exponent.to_signed_int() + 1
|
419
487
|
|
420
|
-
# Step 2:
|
421
|
-
mantissa_self = [True]
|
422
|
-
mantissa_other = [True]
|
488
|
+
# Step 2: Append the implicit leading 1 to form the mantissa
|
489
|
+
mantissa_self = self.fraction + [True]
|
490
|
+
mantissa_other = other.fraction + [True]
|
423
491
|
|
424
492
|
# Step 3: Align mantissas by shifting the smaller exponent
|
425
493
|
result_sign = self.sign
|
@@ -455,9 +523,9 @@ class FlexFloat:
|
|
455
523
|
f"got {len(smaller_mantissa)} bits."
|
456
524
|
)
|
457
525
|
|
458
|
-
mantissa_result =
|
526
|
+
mantissa_result = self._bitarray_implementation.zeros(53)
|
459
527
|
borrow = False
|
460
|
-
for i in range(
|
528
|
+
for i in range(53):
|
461
529
|
diff = int(larger_mantissa[i]) - int(smaller_mantissa[i]) - int(borrow)
|
462
530
|
|
463
531
|
mantissa_result[i] = diff % 2 == 1
|
@@ -468,7 +536,8 @@ class FlexFloat:
|
|
468
536
|
# Step 6: Normalize mantissa and adjust exponent if necessary
|
469
537
|
# Find the first 1 bit (leading bit might have been canceled out)
|
470
538
|
leading_zero_count = next(
|
471
|
-
(i for i, bit in enumerate(mantissa_result) if bit),
|
539
|
+
(i for i, bit in enumerate(reversed(mantissa_result)) if bit),
|
540
|
+
len(mantissa_result),
|
472
541
|
)
|
473
542
|
|
474
543
|
# Handle case where result becomes zero or denormalized
|
@@ -483,23 +552,27 @@ class FlexFloat:
|
|
483
552
|
# Step 7: Grow exponent if necessary (handle underflow)
|
484
553
|
exp_result_length = self._grow_exponent(result_exponent, len(self.exponent))
|
485
554
|
|
486
|
-
exp_result =
|
555
|
+
exp_result = self._bitarray_implementation.from_signed_int(
|
487
556
|
result_exponent - 1, exp_result_length
|
488
557
|
)
|
489
558
|
|
490
559
|
return FlexFloat(
|
491
560
|
sign=result_sign,
|
492
561
|
exponent=exp_result,
|
493
|
-
fraction=mantissa_result[1
|
562
|
+
fraction=mantissa_result[:-1], # Exclude leading bit
|
494
563
|
)
|
495
564
|
|
496
565
|
def __mul__(self, other: FlexFloat | Number) -> FlexFloat:
|
497
|
-
"""
|
566
|
+
"""Multiplies two FlexFloat instances together.
|
498
567
|
|
499
568
|
Args:
|
500
|
-
other (FlexFloat |
|
569
|
+
other (FlexFloat | Number): The other FlexFloat instance to multiply.
|
570
|
+
|
501
571
|
Returns:
|
502
572
|
FlexFloat: A new FlexFloat instance representing the product.
|
573
|
+
|
574
|
+
Raises:
|
575
|
+
TypeError: If other is not a FlexFloat or numeric type.
|
503
576
|
"""
|
504
577
|
if isinstance(other, Number):
|
505
578
|
other = FlexFloat.from_float(other)
|
@@ -543,9 +616,9 @@ class FlexFloat:
|
|
543
616
|
result_exponent = exponent_self + exponent_other
|
544
617
|
|
545
618
|
# Step 4: Multiply mantissas
|
546
|
-
#
|
547
|
-
mantissa_self = [True]
|
548
|
-
mantissa_other = [True]
|
619
|
+
# Append the implicit leading 1 to form the mantissa
|
620
|
+
mantissa_self = self.fraction + [True]
|
621
|
+
mantissa_other = other.fraction + [True]
|
549
622
|
|
550
623
|
# Convert mantissas to integers for multiplication
|
551
624
|
mantissa_self_int = mantissa_self.to_int()
|
@@ -559,8 +632,8 @@ class FlexFloat:
|
|
559
632
|
if product == 0:
|
560
633
|
return FlexFloat.zero()
|
561
634
|
|
562
|
-
product_bits =
|
563
|
-
for i in range(
|
635
|
+
product_bits = self._bitarray_implementation.zeros(106)
|
636
|
+
for i in range(106):
|
564
637
|
product_bits[i] = product & 1 == 1
|
565
638
|
product >>= 1
|
566
639
|
if product <= 0:
|
@@ -568,22 +641,28 @@ class FlexFloat:
|
|
568
641
|
|
569
642
|
# Step 5: Normalize mantissa and adjust exponent if necessary
|
570
643
|
# Find the position of the most significant bit
|
571
|
-
msb_position = next(
|
644
|
+
msb_position = next(
|
645
|
+
(i for i, bit in enumerate(reversed(product_bits)) if bit), None
|
646
|
+
)
|
572
647
|
|
573
648
|
assert msb_position is not None, "Product should not be zero here."
|
574
649
|
|
575
|
-
# The mantissa multiplication gives us a result with 2 integer bits
|
650
|
+
# The mantissa multiplication gives us a result with a 2 integer bits
|
576
651
|
# We need to normalize to have exactly 1 integer bit
|
577
652
|
# If MSB is at position 0, we have a 2-bit integer part (11.xxxxx)
|
578
653
|
# If MSB is at position 1, we have a 1-bit integer part (1.xxxxx)
|
654
|
+
# Mantissa goes from LSB to MSB, so we need to adjust the exponent accordingly
|
579
655
|
if msb_position == 0:
|
580
656
|
result_exponent += 1
|
581
|
-
|
657
|
+
|
658
|
+
# Extract the normalized mantissa
|
659
|
+
lsb_position = 53 - msb_position
|
660
|
+
normalized_mantissa = product_bits[lsb_position : lsb_position + 53]
|
582
661
|
|
583
662
|
# Pad with zeros if we don't have enough bits
|
584
663
|
missing_bits = 53 - len(normalized_mantissa)
|
585
664
|
if missing_bits > 0:
|
586
|
-
normalized_mantissa
|
665
|
+
normalized_mantissa = normalized_mantissa.shift(-missing_bits, fill=False)
|
587
666
|
|
588
667
|
# Step 6: Grow exponent if necessary to accommodate the result
|
589
668
|
exp_result_length = max(len(self.exponent), len(other.exponent))
|
@@ -591,33 +670,38 @@ class FlexFloat:
|
|
591
670
|
# Check if we need to grow the exponent to accommodate the result
|
592
671
|
exp_result_length = self._grow_exponent(result_exponent, exp_result_length)
|
593
672
|
|
594
|
-
exp_result =
|
673
|
+
exp_result = self._bitarray_implementation.from_signed_int(
|
595
674
|
result_exponent - 1, exp_result_length
|
596
675
|
)
|
597
676
|
|
598
677
|
return FlexFloat(
|
599
678
|
sign=result_sign,
|
600
679
|
exponent=exp_result,
|
601
|
-
fraction=normalized_mantissa[1
|
680
|
+
fraction=normalized_mantissa[:-1], # Exclude leading bit
|
602
681
|
)
|
603
682
|
|
604
683
|
def __rmul__(self, other: Number) -> FlexFloat:
|
605
684
|
"""Right-hand multiplication for Number types.
|
606
685
|
|
607
686
|
Args:
|
608
|
-
other (
|
687
|
+
other (Number): The number to multiply with this FlexFloat.
|
688
|
+
|
609
689
|
Returns:
|
610
690
|
FlexFloat: A new FlexFloat instance representing the product.
|
611
691
|
"""
|
612
|
-
return self * other
|
692
|
+
return self * FlexFloat.from_float(other)
|
613
693
|
|
614
694
|
def __truediv__(self, other: FlexFloat | Number) -> FlexFloat:
|
615
|
-
"""
|
695
|
+
"""Divides this FlexFloat by another FlexFloat or number.
|
616
696
|
|
617
697
|
Args:
|
618
|
-
other (FlexFloat |
|
698
|
+
other (FlexFloat | Number): The divisor.
|
699
|
+
|
619
700
|
Returns:
|
620
701
|
FlexFloat: A new FlexFloat instance representing the quotient.
|
702
|
+
|
703
|
+
Raises:
|
704
|
+
TypeError: If other is not a FlexFloat or numeric type.
|
621
705
|
"""
|
622
706
|
if isinstance(other, Number):
|
623
707
|
other = FlexFloat.from_float(other)
|
@@ -670,9 +754,9 @@ class FlexFloat:
|
|
670
754
|
result_exponent = exponent_self - exponent_other
|
671
755
|
|
672
756
|
# Step 4: Divide mantissas
|
673
|
-
#
|
674
|
-
mantissa_self = [True]
|
675
|
-
mantissa_other = [True]
|
757
|
+
# Append the implicit leading 1 to form the mantissa
|
758
|
+
mantissa_self = self.fraction + [True]
|
759
|
+
mantissa_other = other.fraction + [True]
|
676
760
|
|
677
761
|
# Convert mantissas to integers for division
|
678
762
|
mantissa_self_int = mantissa_self.to_int()
|
@@ -691,26 +775,32 @@ class FlexFloat:
|
|
691
775
|
return FlexFloat.zero()
|
692
776
|
|
693
777
|
# Convert quotient to BitArray for easier bit manipulation
|
694
|
-
|
778
|
+
# Use a fixed size for consistency
|
779
|
+
quotient_bitarray = self._bitarray_implementation.zeros(64)
|
695
780
|
temp_quotient = quotient
|
696
|
-
bit_pos =
|
697
|
-
while temp_quotient > 0 and bit_pos
|
781
|
+
bit_pos = 0
|
782
|
+
while temp_quotient > 0 and bit_pos < 64:
|
698
783
|
quotient_bitarray[bit_pos] = (temp_quotient & 1) == 1
|
699
784
|
temp_quotient >>= 1
|
700
|
-
bit_pos
|
785
|
+
bit_pos += 1
|
701
786
|
|
702
787
|
# Step 5: Normalize mantissa and adjust exponent if necessary
|
703
788
|
# Find the position of the most significant bit (first 1)
|
704
|
-
msb_pos = next(
|
789
|
+
msb_pos = next(
|
790
|
+
(i for i, bit in enumerate(reversed(quotient_bitarray)) if bit), None
|
791
|
+
)
|
705
792
|
|
706
793
|
if msb_pos is None:
|
707
794
|
return FlexFloat.zero()
|
708
795
|
|
709
796
|
# Extract exactly 53 bits starting from the MSB (1 integer + 52 fraction)
|
710
|
-
|
711
|
-
normalized_mantissa =
|
712
|
-
|
713
|
-
|
797
|
+
lsb_pos = 11 - msb_pos
|
798
|
+
normalized_mantissa = quotient_bitarray[lsb_pos : lsb_pos + 53]
|
799
|
+
|
800
|
+
# If we don't have enough bits, pad with zeros
|
801
|
+
missing_bits = 53 - len(normalized_mantissa)
|
802
|
+
if missing_bits > 0:
|
803
|
+
normalized_mantissa = normalized_mantissa.shift(-missing_bits, fill=False)
|
714
804
|
|
715
805
|
# Step 6: Grow exponent if necessary to accommodate the result
|
716
806
|
exp_result_length = max(len(self.exponent), len(other.exponent))
|
@@ -718,32 +808,33 @@ class FlexFloat:
|
|
718
808
|
# Check if we need to grow the exponent to accommodate the result
|
719
809
|
exp_result_length = self._grow_exponent(result_exponent, exp_result_length)
|
720
810
|
|
721
|
-
exp_result =
|
811
|
+
exp_result = self._bitarray_implementation.from_signed_int(
|
722
812
|
result_exponent - 1, exp_result_length
|
723
813
|
)
|
724
814
|
|
725
815
|
return FlexFloat(
|
726
816
|
sign=result_sign,
|
727
817
|
exponent=exp_result,
|
728
|
-
fraction=normalized_mantissa[1
|
818
|
+
fraction=normalized_mantissa[:-1], # Exclude leading bit
|
729
819
|
)
|
730
820
|
|
731
821
|
def __rtruediv__(self, other: Number) -> FlexFloat:
|
732
822
|
"""Right-hand division for Number types.
|
733
823
|
|
734
824
|
Args:
|
735
|
-
other (
|
825
|
+
other (Number): The number to divide by this FlexFloat.
|
826
|
+
|
736
827
|
Returns:
|
737
828
|
FlexFloat: A new FlexFloat instance representing the quotient.
|
738
829
|
"""
|
739
830
|
return FlexFloat.from_float(other) / self
|
740
831
|
|
741
832
|
def __abs__(self) -> FlexFloat:
|
742
|
-
"""
|
833
|
+
"""Returns the absolute value of the FlexFloat instance.
|
743
834
|
|
744
835
|
Returns:
|
745
|
-
FlexFloat: A new FlexFloat instance with the same exponent and fraction,
|
746
|
-
|
836
|
+
FlexFloat: A new FlexFloat instance with the same exponent and fraction, but
|
837
|
+
with the sign set to False (positive).
|
747
838
|
"""
|
748
839
|
return FlexFloat(
|
749
840
|
sign=False,
|
@@ -752,31 +843,35 @@ class FlexFloat:
|
|
752
843
|
)
|
753
844
|
|
754
845
|
def abs(self) -> FlexFloat:
|
755
|
-
"""
|
846
|
+
"""Calculates the absolute value of the FlexFloat instance.
|
756
847
|
|
757
848
|
Returns:
|
758
|
-
FlexFloat: A new FlexFloat instance with the same exponent and fraction,
|
759
|
-
|
849
|
+
FlexFloat: A new FlexFloat instance with the same exponent and fraction, but
|
850
|
+
with the sign set to False (positive).
|
760
851
|
"""
|
761
852
|
return abs(self)
|
762
853
|
|
763
854
|
def exp(self) -> FlexFloat:
|
764
|
-
"""
|
855
|
+
"""Calculates the exponential of the FlexFloat instance.
|
765
856
|
|
766
857
|
Returns:
|
767
|
-
FlexFloat: A new FlexFloat instance representing e raised to the power
|
768
|
-
|
858
|
+
FlexFloat: A new FlexFloat instance representing e raised to the power of
|
859
|
+
this instance.
|
769
860
|
"""
|
770
861
|
# Use Python's math.exp for the base calculation
|
771
862
|
return FlexFloat.e**self
|
772
863
|
|
773
864
|
def __pow__(self, other: FlexFloat | Number) -> FlexFloat:
|
774
|
-
"""
|
865
|
+
"""Raises this FlexFloat to the power of another FlexFloat or number.
|
775
866
|
|
776
867
|
Args:
|
777
868
|
other (FlexFloat | Number): The exponent.
|
869
|
+
|
778
870
|
Returns:
|
779
871
|
FlexFloat: A new FlexFloat instance representing the power.
|
872
|
+
|
873
|
+
Raises:
|
874
|
+
TypeError: If other is not a FlexFloat or numeric type.
|
780
875
|
"""
|
781
876
|
if isinstance(other, Number):
|
782
877
|
other = FlexFloat.from_float(other)
|
@@ -913,12 +1008,14 @@ class FlexFloat:
|
|
913
1008
|
required_exp_bits = max(11, abs(estimated_exp).bit_length() + 2)
|
914
1009
|
|
915
1010
|
# Create a small result with extended exponent
|
916
|
-
small_exp =
|
1011
|
+
small_exp = self._bitarray_implementation.from_signed_int(
|
917
1012
|
estimated_exp, required_exp_bits
|
918
1013
|
)
|
919
1014
|
# Use a normalized mantissa (leading 1 + some fraction bits)
|
920
1015
|
result = FlexFloat(
|
921
|
-
sign=False,
|
1016
|
+
sign=False,
|
1017
|
+
exponent=small_exp,
|
1018
|
+
fraction=self._bitarray_implementation.ones(52),
|
922
1019
|
)
|
923
1020
|
else:
|
924
1021
|
# Compute the power using Python's built-in pow
|
@@ -932,13 +1029,13 @@ class FlexFloat:
|
|
932
1029
|
if log_result_estimate < -300: # Very small but not quite underflow
|
933
1030
|
estimated_exp = int(log_result_estimate / LOG10_2)
|
934
1031
|
required_exp_bits = max(11, abs(estimated_exp).bit_length() + 2)
|
935
|
-
small_exp =
|
1032
|
+
small_exp = self._bitarray_implementation.from_signed_int(
|
936
1033
|
estimated_exp, required_exp_bits
|
937
1034
|
)
|
938
1035
|
result = FlexFloat(
|
939
1036
|
sign=False,
|
940
1037
|
exponent=small_exp,
|
941
|
-
fraction=
|
1038
|
+
fraction=self._bitarray_implementation.ones(52),
|
942
1039
|
)
|
943
1040
|
else:
|
944
1041
|
return FlexFloat.zero()
|
@@ -974,19 +1071,23 @@ class FlexFloat:
|
|
974
1071
|
# use arbitrary precision arithmetic
|
975
1072
|
if estimated_exp_float > 0:
|
976
1073
|
# Very large positive result
|
977
|
-
large_exp =
|
1074
|
+
large_exp = self._bitarray_implementation.from_signed_int(
|
978
1075
|
int(estimated_exp_float), required_exp_bits
|
979
1076
|
)
|
980
1077
|
result = FlexFloat(
|
981
|
-
sign=False,
|
1078
|
+
sign=False,
|
1079
|
+
exponent=large_exp,
|
1080
|
+
fraction=self._bitarray_implementation.ones(52),
|
982
1081
|
)
|
983
1082
|
else:
|
984
1083
|
# Very small positive result
|
985
|
-
small_exp =
|
1084
|
+
small_exp = self._bitarray_implementation.from_signed_int(
|
986
1085
|
int(estimated_exp_float), required_exp_bits
|
987
1086
|
)
|
988
1087
|
result = FlexFloat(
|
989
|
-
sign=False,
|
1088
|
+
sign=False,
|
1089
|
+
exponent=small_exp,
|
1090
|
+
fraction=self._bitarray_implementation.ones(52),
|
990
1091
|
)
|
991
1092
|
|
992
1093
|
except (ValueError, OverflowError, ZeroDivisionError):
|
@@ -1003,7 +1104,8 @@ class FlexFloat:
|
|
1003
1104
|
"""Right-hand power operation for Number types.
|
1004
1105
|
|
1005
1106
|
Args:
|
1006
|
-
base (
|
1107
|
+
base (Number): The base to raise to the power of this FlexFloat.
|
1108
|
+
|
1007
1109
|
Returns:
|
1008
1110
|
FlexFloat: A new FlexFloat instance representing the power.
|
1009
1111
|
"""
|