flexfloat 0.1.0__py3-none-any.whl → 0.1.1__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 +1 -1
- flexfloat/bitarray.py +12 -8
- flexfloat/core.py +89 -39
- {flexfloat-0.1.0.dist-info → flexfloat-0.1.1.dist-info}/METADATA +1 -1
- flexfloat-0.1.1.dist-info/RECORD +9 -0
- flexfloat-0.1.0.dist-info/RECORD +0 -9
- {flexfloat-0.1.0.dist-info → flexfloat-0.1.1.dist-info}/WHEEL +0 -0
- {flexfloat-0.1.0.dist-info → flexfloat-0.1.1.dist-info}/top_level.txt +0 -0
flexfloat/__init__.py
CHANGED
flexfloat/bitarray.py
CHANGED
@@ -9,8 +9,8 @@ from typing import Iterator, overload
|
|
9
9
|
class BitArray:
|
10
10
|
"""A bit array class that encapsulates a list of booleans with utility methods.
|
11
11
|
|
12
|
-
This class provides all the functionality previously available through utility
|
13
|
-
now encapsulated as methods for better object-oriented design.
|
12
|
+
This class provides all the functionality previously available through utility
|
13
|
+
functions, now encapsulated as methods for better object-oriented design.
|
14
14
|
"""
|
15
15
|
|
16
16
|
def __init__(self, bits: list[bool] | None = None):
|
@@ -119,7 +119,8 @@ class BitArray:
|
|
119
119
|
return sum((1 << i) for i, bit in enumerate(reversed(self._bits)) if bit)
|
120
120
|
|
121
121
|
def to_signed_int(self) -> int:
|
122
|
-
"""Convert a bit array into a signed integer using off-set binary
|
122
|
+
"""Convert a bit array into a signed integer using off-set binary
|
123
|
+
representation.
|
123
124
|
|
124
125
|
Returns:
|
125
126
|
int: The signed integer represented by the bit array.
|
@@ -137,13 +138,16 @@ class BitArray:
|
|
137
138
|
def shift(self, shift_amount: int, fill: bool = False) -> BitArray:
|
138
139
|
"""Shift the bit array left or right by a specified number of bits.
|
139
140
|
|
140
|
-
This function shifts the bits in the array, filling in new bits with the
|
141
|
-
|
142
|
-
If the
|
141
|
+
This function shifts the bits in the array, filling in new bits with the
|
142
|
+
specified fill value.
|
143
|
+
If the value is positive, it shifts left; if negative, it shifts right.
|
144
|
+
Fills the new bits with the specified fill value (default is False).
|
143
145
|
|
144
146
|
Args:
|
145
|
-
shift_amount (int): The number of bits to shift. Positive for left shift,
|
146
|
-
|
147
|
+
shift_amount (int): The number of bits to shift. Positive for left shift,
|
148
|
+
negative for right shift.
|
149
|
+
fill (bool): The value to fill in the new bits created by the shift.
|
150
|
+
Defaults to False.
|
147
151
|
Returns:
|
148
152
|
BitArray: A new BitArray with the bits shifted and filled.
|
149
153
|
"""
|
flexfloat/core.py
CHANGED
@@ -2,22 +2,30 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
import math
|
6
|
+
from typing import Final
|
7
|
+
|
5
8
|
from .bitarray import BitArray
|
6
9
|
from .types import Number
|
7
10
|
|
11
|
+
LOG10_2: Final[float] = math.log10(2)
|
12
|
+
|
8
13
|
|
9
14
|
class FlexFloat:
|
10
|
-
"""A class to represent a floating-point number with growable exponent and
|
11
|
-
This class is designed to handle very large or very
|
12
|
-
|
15
|
+
"""A class to represent a floating-point number with growable exponent and
|
16
|
+
fixed-size fraction. This class is designed to handle very large or very
|
17
|
+
small numbers by adjusting the exponent dynamically. While keeping the
|
18
|
+
mantissa (fraction) fixed in size.
|
13
19
|
|
14
20
|
This class follows the IEEE 754 double-precision floating-point format,
|
15
21
|
but extends it to allow for a growable exponent and a fixed-size fraction.
|
16
22
|
|
17
23
|
Attributes:
|
18
24
|
sign (bool): The sign of the number (True for negative, False for positive).
|
19
|
-
exponent (BitArray): A growable bit array representing the exponent
|
20
|
-
|
25
|
+
exponent (BitArray): A growable bit array representing the exponent
|
26
|
+
(uses off-set binary representation).
|
27
|
+
fraction (BitArray): A fixed-size bit array representing the fraction
|
28
|
+
(mantissa) of the number.
|
21
29
|
"""
|
22
30
|
|
23
31
|
def __init__(
|
@@ -30,8 +38,8 @@ class FlexFloat:
|
|
30
38
|
|
31
39
|
Args:
|
32
40
|
sign (bool): The sign of the number (True for negative, False for positive).
|
33
|
-
exponent (BitArray | None): The exponent bit array
|
34
|
-
fraction (BitArray | None): The fraction bit array
|
41
|
+
exponent (BitArray | None): The exponent bit array (If None, represents 0).
|
42
|
+
fraction (BitArray | None): The fraction bit array (If None, represents 0).
|
35
43
|
"""
|
36
44
|
self.sign = sign
|
37
45
|
self.exponent = exponent if exponent is not None else BitArray.zeros(11)
|
@@ -54,7 +62,7 @@ class FlexFloat:
|
|
54
62
|
def to_float(self) -> float:
|
55
63
|
"""Convert the FlexFloat instance back to a 64-bit float.
|
56
64
|
|
57
|
-
If float is bigger than 64 bits, it will truncate the value to fit
|
65
|
+
If float is bigger than 64 bits, it will truncate the value to fit.
|
58
66
|
|
59
67
|
Returns:
|
60
68
|
float: The floating-point number represented by the FlexFloat instance.
|
@@ -73,7 +81,12 @@ class FlexFloat:
|
|
73
81
|
Returns:
|
74
82
|
str: A string representation of the FlexFloat instance.
|
75
83
|
"""
|
76
|
-
return
|
84
|
+
return (
|
85
|
+
"FlexFloat("
|
86
|
+
f"sign={self.sign}, "
|
87
|
+
f"exponent={self.exponent}, "
|
88
|
+
f"fraction={self.fraction})"
|
89
|
+
)
|
77
90
|
|
78
91
|
def pretty(self) -> str:
|
79
92
|
"""Return an easier to read string representation of the FlexFloat instance.
|
@@ -103,7 +116,7 @@ class FlexFloat:
|
|
103
116
|
"""Create a FlexFloat instance representing Infinity.
|
104
117
|
|
105
118
|
Args:
|
106
|
-
sign (bool):
|
119
|
+
sign (bool): Indicates if the infinity is negative.
|
107
120
|
Returns:
|
108
121
|
FlexFloat: A new FlexFloat instance representing Infinity.
|
109
122
|
"""
|
@@ -130,7 +143,8 @@ class FlexFloat:
|
|
130
143
|
"""
|
131
144
|
# In IEEE 754, special values have all exponent bits set to 1
|
132
145
|
# This corresponds to the maximum value in the unsigned representation
|
133
|
-
# For signed offset binary, the maximum value is 2^(n-1) - 1
|
146
|
+
# For signed offset binary, the maximum value is 2^(n-1) - 1
|
147
|
+
# where n is the number of bits
|
134
148
|
max_signed_value = (1 << (len(self.exponent) - 1)) - 1
|
135
149
|
return self.exponent.to_signed_int() == max_signed_value
|
136
150
|
|
@@ -162,36 +176,60 @@ class FlexFloat:
|
|
162
176
|
"""Create a copy of the FlexFloat instance.
|
163
177
|
|
164
178
|
Returns:
|
165
|
-
FlexFloat: A new FlexFloat instance with the same
|
179
|
+
FlexFloat: A new FlexFloat instance with the same data as the original.
|
166
180
|
"""
|
167
181
|
return FlexFloat(
|
168
182
|
sign=self.sign, exponent=self.exponent.copy(), fraction=self.fraction.copy()
|
169
183
|
)
|
170
184
|
|
171
185
|
def __str__(self) -> str:
|
172
|
-
"""Float representation of the FlexFloat.
|
173
|
-
|
186
|
+
"""Float representation of the FlexFloat using a generic algorithm.
|
187
|
+
|
188
|
+
This implementation doesn't rely on Python's float conversion and instead
|
189
|
+
implements the formatting logic directly, making it work for any exponent size.
|
190
|
+
|
191
|
+
Currently, it only operates in scientific notation with 5 decimal places.
|
192
|
+
"""
|
193
|
+
sign_str = "-" if self.sign else ""
|
194
|
+
# Handle special cases first
|
195
|
+
if self.is_nan():
|
196
|
+
return "nan"
|
197
|
+
|
198
|
+
if self.is_infinity():
|
199
|
+
return f"{sign_str}inf"
|
174
200
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
return f"{sign}Infinity"
|
184
|
-
|
185
|
-
fraction_value: float = 1
|
201
|
+
if self.is_zero():
|
202
|
+
return f"{sign_str}0.00000e+00"
|
203
|
+
|
204
|
+
exponent = self.exponent.to_signed_int() + 1
|
205
|
+
|
206
|
+
# Convert fraction to decimal value between 1 and 2
|
207
|
+
# (starting with 1.0 for the implicit leading bit)
|
208
|
+
mantissa = 1.0
|
186
209
|
for i, bit in enumerate(self.fraction):
|
187
210
|
if bit:
|
188
|
-
|
211
|
+
mantissa += 1.0 / (1 << (i + 1))
|
212
|
+
|
213
|
+
# To avoid overflow with very large exponents, work in log space
|
214
|
+
# log10(mantissa * 2^exponent) = log10(mantissa) + exponent * log10(2)
|
215
|
+
log10_mantissa = math.log10(mantissa)
|
216
|
+
log10_total = log10_mantissa + exponent * LOG10_2
|
217
|
+
|
218
|
+
decimal_exponent = int(log10_total)
|
219
|
+
|
220
|
+
log10_normalized = log10_total - decimal_exponent
|
221
|
+
normalized_mantissa = math.pow(10, log10_normalized)
|
189
222
|
|
190
|
-
|
191
|
-
|
223
|
+
# Ensure the mantissa is properly normalized (between 1.0 and 10.0)
|
224
|
+
while normalized_mantissa >= 10.0:
|
225
|
+
normalized_mantissa /= 10.0
|
226
|
+
decimal_exponent += 1
|
227
|
+
while normalized_mantissa < 1.0:
|
228
|
+
normalized_mantissa *= 10.0
|
229
|
+
decimal_exponent -= 1
|
192
230
|
|
193
|
-
#
|
194
|
-
return ""
|
231
|
+
# Format with 5 decimal places
|
232
|
+
return f"{sign_str}{normalized_mantissa:.5f}e{decimal_exponent:+03d}"
|
195
233
|
|
196
234
|
def __neg__(self) -> FlexFloat:
|
197
235
|
"""Negate the FlexFloat instance."""
|
@@ -209,7 +247,8 @@ class FlexFloat:
|
|
209
247
|
exponent (int): The current exponent value.
|
210
248
|
exponent_length (int): The current length of the exponent in bits.
|
211
249
|
Returns:
|
212
|
-
int: The new exponent length if it needs to be grown, otherwise the same
|
250
|
+
int: The new exponent length if it needs to be grown, otherwise the same
|
251
|
+
length.
|
213
252
|
"""
|
214
253
|
while True:
|
215
254
|
half = 1 << (exponent_length - 1)
|
@@ -239,7 +278,7 @@ class FlexFloat:
|
|
239
278
|
return self - (-other)
|
240
279
|
|
241
280
|
# OBJECTIVE: Add two FlexFloat instances together.
|
242
|
-
#
|
281
|
+
# https://www.sciencedirect.com/topics/computer-science/floating-point-addition
|
243
282
|
# and: https://cse.hkust.edu.hk/~cktang/cs180/notes/lec21.pdf
|
244
283
|
#
|
245
284
|
# Steps:
|
@@ -285,8 +324,13 @@ class FlexFloat:
|
|
285
324
|
mantissa_other = mantissa_other.shift(shift_amount)
|
286
325
|
|
287
326
|
# Step 5: Add mantissas
|
288
|
-
assert
|
289
|
-
|
327
|
+
assert (
|
328
|
+
len(mantissa_self) == 53
|
329
|
+
), "Fraction must be 53 bits long. (1 leading bit + 52 fraction bits)"
|
330
|
+
assert len(mantissa_self) == len(mantissa_other), (
|
331
|
+
f"Mantissas must be the same length. Expected 53 bits, "
|
332
|
+
f"got {len(mantissa_other)} bits."
|
333
|
+
)
|
290
334
|
|
291
335
|
mantissa_result = BitArray.zeros(53) # 1 leading bit + 52 fraction bits
|
292
336
|
carry = False
|
@@ -398,8 +442,13 @@ class FlexFloat:
|
|
398
442
|
result_sign = not self.sign
|
399
443
|
|
400
444
|
# Step 5: Subtract mantissas (larger - smaller)
|
401
|
-
assert
|
402
|
-
|
445
|
+
assert (
|
446
|
+
len(larger_mantissa) == 53
|
447
|
+
), "Mantissa must be 53 bits long. (1 leading bit + 52 fraction bits)"
|
448
|
+
assert len(larger_mantissa) == len(smaller_mantissa), (
|
449
|
+
f"Mantissas must be the same length. Expected 53 bits, "
|
450
|
+
f"got {len(smaller_mantissa)} bits."
|
451
|
+
)
|
403
452
|
|
404
453
|
mantissa_result = BitArray.zeros(53)
|
405
454
|
borrow = False
|
@@ -451,7 +500,7 @@ class FlexFloat:
|
|
451
500
|
raise TypeError("Can only multiply FlexFloat instances.")
|
452
501
|
|
453
502
|
# OBJECTIVE: Multiply two FlexFloat instances together.
|
454
|
-
#
|
503
|
+
# https://www.rfwireless-world.com/tutorials/ieee-754-floating-point-arithmetic
|
455
504
|
#
|
456
505
|
# Steps:
|
457
506
|
# 0. Handle special cases (NaN, Infinity, zero).
|
@@ -567,7 +616,7 @@ class FlexFloat:
|
|
567
616
|
raise TypeError("Can only divide FlexFloat instances.")
|
568
617
|
|
569
618
|
# OBJECTIVE: Divide two FlexFloat instances.
|
570
|
-
#
|
619
|
+
# https://www.rfwireless-world.com/tutorials/ieee-754-floating-point-arithmetic
|
571
620
|
#
|
572
621
|
# Steps:
|
573
622
|
# 0. Handle special cases (NaN, Infinity, zero).
|
@@ -603,7 +652,8 @@ class FlexFloat:
|
|
603
652
|
result_sign = self.sign ^ other.sign
|
604
653
|
|
605
654
|
# Step 2: Extract exponent and fraction bits
|
606
|
-
# Note: The stored exponent needs +1 to get the actual value
|
655
|
+
# Note: The stored exponent needs +1 to get the actual value
|
656
|
+
# (like in multiplication)
|
607
657
|
exponent_self = self.exponent.to_signed_int() + 1
|
608
658
|
exponent_other = other.exponent.to_signed_int() + 1
|
609
659
|
|
@@ -0,0 +1,9 @@
|
|
1
|
+
flexfloat/__init__.py,sha256=SFtec_6Rr65_Sr83rhoh9VT7Egj8N-e2F0fyMQbBqKg,378
|
2
|
+
flexfloat/bitarray.py,sha256=hlJtoe50Lx6KdNTyMNFkJCBON4t_gtCDc3hWfX_bza4,9196
|
3
|
+
flexfloat/core.py,sha256=7bnw7aJvnFOD1RTtZvH9nc4QCKcSowqsSGhcncAMlJI,28771
|
4
|
+
flexfloat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
flexfloat/types.py,sha256=SFTYmG3BHLiCJc0LvNwOnByRLRcRXH2erINeqLyHLKs,118
|
6
|
+
flexfloat-0.1.1.dist-info/METADATA,sha256=SMM9FyWRr08gI32_gssW0bkCLxPF-MTm9lim3J_-Wdk,2459
|
7
|
+
flexfloat-0.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
+
flexfloat-0.1.1.dist-info/top_level.txt,sha256=82S8dY2UoNZh-9pwg7tUvbwB3uw2s3mfEoyUW6vCMdU,10
|
9
|
+
flexfloat-0.1.1.dist-info/RECORD,,
|
flexfloat-0.1.0.dist-info/RECORD
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
flexfloat/__init__.py,sha256=fvimTZm80CHm8bp11bID5twYoISERcatgx21snQxsz8,378
|
2
|
-
flexfloat/bitarray.py,sha256=Ss0bZUUpfsBPJkPaGymXd2O-4e-UCanUva_encnc4MI,9172
|
3
|
-
flexfloat/core.py,sha256=KK8LfRlqReZuVgyN-fQwYDsM1TlaJtYjHcafPYSPZeQ,27570
|
4
|
-
flexfloat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
flexfloat/types.py,sha256=SFTYmG3BHLiCJc0LvNwOnByRLRcRXH2erINeqLyHLKs,118
|
6
|
-
flexfloat-0.1.0.dist-info/METADATA,sha256=8QUYOP_vFgljckHUm02jixPFfDYkt-XZdW5Wahm8TbQ,2459
|
7
|
-
flexfloat-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
8
|
-
flexfloat-0.1.0.dist-info/top_level.txt,sha256=82S8dY2UoNZh-9pwg7tUvbwB3uw2s3mfEoyUW6vCMdU,10
|
9
|
-
flexfloat-0.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|