flexfloat 0.1.2__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/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 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)
@@ -28,55 +42,98 @@ class FlexFloat:
28
42
  (mantissa) of the number.
29
43
  """
30
44
 
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
66
+
31
67
  def __init__(
32
68
  self,
33
69
  sign: bool = False,
34
70
  exponent: BitArray | None = None,
35
71
  fraction: BitArray | None = None,
36
72
  ):
37
- """Initialize a FlexFloat instance.
73
+ """Initializes a FlexFloat instance.
74
+
75
+ BitArrays are expected to be LSB-first (least significant bit at index 0,
76
+ increasing to MSB).
38
77
 
39
78
  Args:
40
- sign (bool): The sign of the number (True for negative, False for positive).
41
- exponent (BitArray | None): The exponent bit array (If None, represents 0).
42
- fraction (BitArray | None): The fraction bit array (If None, represents 0).
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.
43
85
  """
44
86
  self.sign = sign
45
- self.exponent = exponent if exponent is not None else BitArray.zeros(11)
46
- self.fraction = fraction if fraction is not None else BitArray.zeros(52)
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
+ )
47
97
 
48
98
  @classmethod
49
99
  def from_float(cls, value: Number) -> FlexFloat:
50
- """Create a FlexFloat instance from a number.
100
+ """Creates a FlexFloat instance from a number.
51
101
 
52
102
  Args:
53
103
  value (Number): The number to convert to FlexFloat.
104
+
54
105
  Returns:
55
106
  FlexFloat: A new FlexFloat instance representing the number.
56
107
  """
57
108
  value = float(value)
58
- bits = BitArray.from_float(value)
109
+ bits = cls._bitarray_implementation.from_float(value)
59
110
 
60
- return cls(sign=bits[0], exponent=bits[1:12], fraction=bits[12:64])
111
+ return cls(sign=bits[63], exponent=bits[52:63], fraction=bits[:52])
61
112
 
62
113
  def to_float(self) -> float:
63
- """Convert the FlexFloat instance back to a 64-bit float.
114
+ """Converts the FlexFloat instance back to a 64-bit float.
64
115
 
65
116
  If float is bigger than 64 bits, it will truncate the value to fit.
66
117
 
67
118
  Returns:
68
119
  float: The floating-point number represented by the FlexFloat instance.
120
+
69
121
  Raises:
70
- ValueError: If the exponent or fraction lengths are not as expected.
122
+ ValueError: If the FlexFloat does not have standard 64-bit exponent and
123
+ fraction.
71
124
  """
72
125
  if len(self.exponent) < 11 or len(self.fraction) < 52:
73
126
  raise ValueError("Must be a standard 64-bit FlexFloat")
74
127
 
75
- bits = BitArray([self.sign]) + self.exponent[:11] + self.fraction[:52]
128
+ bits = (
129
+ self.fraction[:52]
130
+ + self.exponent[:11]
131
+ + self._bitarray_implementation.from_bits([self.sign])
132
+ )
76
133
  return bits.to_float()
77
134
 
78
135
  def __repr__(self) -> str:
79
- """Return a string representation of the FlexFloat instance.
136
+ """Returns a string representation of the FlexFloat instance.
80
137
 
81
138
  Returns:
82
139
  str: A string representation of the FlexFloat instance.
@@ -89,7 +146,7 @@ class FlexFloat:
89
146
  )
90
147
 
91
148
  def pretty(self) -> str:
92
- """Return an easier to read string representation of the FlexFloat instance.
149
+ """Returns an easier to read string representation of the FlexFloat instance.
93
150
  Mainly converts the exponent and fraction to integers for readability.
94
151
 
95
152
  Returns:
@@ -102,54 +159,52 @@ class FlexFloat:
102
159
 
103
160
  @classmethod
104
161
  def nan(cls) -> FlexFloat:
105
- """Create a FlexFloat instance representing NaN (Not a Number).
162
+ """Creates a FlexFloat instance representing NaN (Not a Number).
106
163
 
107
164
  Returns:
108
165
  FlexFloat: A new FlexFloat instance representing NaN.
109
166
  """
110
- exponent = BitArray.ones(11)
111
- fraction = BitArray.ones(52)
167
+ exponent = cls._bitarray_implementation.ones(11)
168
+ fraction = cls._bitarray_implementation.ones(52)
112
169
  return cls(sign=True, exponent=exponent, fraction=fraction)
113
170
 
114
171
  @classmethod
115
172
  def infinity(cls, sign: bool = False) -> FlexFloat:
116
- """Create a FlexFloat instance representing Infinity.
173
+ """Creates a FlexFloat instance representing Infinity.
117
174
 
118
175
  Args:
119
- sign (bool): Indicates if the infinity is negative.
176
+ sign (bool, optional): Indicates if the infinity is negative. Defaults to
177
+ False.
178
+
120
179
  Returns:
121
180
  FlexFloat: A new FlexFloat instance representing Infinity.
122
181
  """
123
- exponent = BitArray.ones(11)
124
- fraction = BitArray.zeros(52)
182
+ exponent = cls._bitarray_implementation.ones(11)
183
+ fraction = cls._bitarray_implementation.zeros(52)
125
184
  return cls(sign=sign, exponent=exponent, fraction=fraction)
126
185
 
127
186
  @classmethod
128
187
  def zero(cls) -> FlexFloat:
129
- """Create a FlexFloat instance representing zero.
188
+ """Creates a FlexFloat instance representing zero.
130
189
 
131
190
  Returns:
132
191
  FlexFloat: A new FlexFloat instance representing zero.
133
192
  """
134
- exponent = BitArray.zeros(11)
135
- fraction = BitArray.zeros(52)
193
+ exponent = cls._bitarray_implementation.zeros(11)
194
+ fraction = cls._bitarray_implementation.zeros(52)
136
195
  return cls(sign=False, exponent=exponent, fraction=fraction)
137
196
 
138
197
  def _is_special_exponent(self) -> bool:
139
- """Check if the exponent represents a special value (NaN or Infinity).
198
+ """Checks if the exponent represents a special value (NaN or Infinity).
140
199
 
141
200
  Returns:
142
201
  bool: True if the exponent is at its maximum value, False otherwise.
143
202
  """
144
- # In IEEE 754, special values have all exponent bits set to 1
145
- # This corresponds to the maximum value in the unsigned representation
146
- # For signed offset binary, the maximum value is 2^(n-1) - 1
147
- # where n is the number of bits
148
203
  max_signed_value = (1 << (len(self.exponent) - 1)) - 1
149
204
  return self.exponent.to_signed_int() == max_signed_value
150
205
 
151
206
  def is_nan(self) -> bool:
152
- """Check if the FlexFloat instance represents NaN (Not a Number).
207
+ """Checks if the FlexFloat instance represents NaN (Not a Number).
153
208
 
154
209
  Returns:
155
210
  bool: True if the FlexFloat instance is NaN, False otherwise.
@@ -157,7 +212,7 @@ class FlexFloat:
157
212
  return self._is_special_exponent() and any(self.fraction)
158
213
 
159
214
  def is_infinity(self) -> bool:
160
- """Check if the FlexFloat instance represents Infinity.
215
+ """Checks if the FlexFloat instance represents Infinity.
161
216
 
162
217
  Returns:
163
218
  bool: True if the FlexFloat instance is Infinity, False otherwise.
@@ -165,7 +220,7 @@ class FlexFloat:
165
220
  return self._is_special_exponent() and not any(self.fraction)
166
221
 
167
222
  def is_zero(self) -> bool:
168
- """Check if the FlexFloat instance represents zero.
223
+ """Checks if the FlexFloat instance represents zero.
169
224
 
170
225
  Returns:
171
226
  bool: True if the FlexFloat instance is zero, False otherwise.
@@ -173,7 +228,7 @@ class FlexFloat:
173
228
  return not any(self.exponent) and not any(self.fraction)
174
229
 
175
230
  def copy(self) -> FlexFloat:
176
- """Create a copy of the FlexFloat instance.
231
+ """Creates a copy of the FlexFloat instance.
177
232
 
178
233
  Returns:
179
234
  FlexFloat: A new FlexFloat instance with the same data as the original.
@@ -183,12 +238,13 @@ class FlexFloat:
183
238
  )
184
239
 
185
240
  def __str__(self) -> str:
186
- """Float representation of the FlexFloat using a generic algorithm.
241
+ """Returns a float representation of the FlexFloat using a generic algorithm.
187
242
 
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.
243
+ Currently, it only operates in one format: scientific notation with 5 decimal
244
+ places.
190
245
 
191
- Currently, it only operates in scientific notation with 5 decimal places.
246
+ Returns:
247
+ str: The string representation in scientific notation.
192
248
  """
193
249
  sign_str = "-" if self.sign else ""
194
250
  # Handle special cases first
@@ -206,7 +262,7 @@ class FlexFloat:
206
262
  # Convert fraction to decimal value between 1 and 2
207
263
  # (starting with 1.0 for the implicit leading bit)
208
264
  mantissa = 1.0
209
- for i, bit in enumerate(self.fraction):
265
+ for i, bit in enumerate(reversed(self.fraction)):
210
266
  if bit:
211
267
  mantissa += 1.0 / (1 << (i + 1))
212
268
 
@@ -224,6 +280,7 @@ class FlexFloat:
224
280
  while normalized_mantissa >= 10.0:
225
281
  normalized_mantissa /= 10.0
226
282
  decimal_exponent += 1
283
+
227
284
  while normalized_mantissa < 1.0:
228
285
  normalized_mantissa *= 10.0
229
286
  decimal_exponent -= 1
@@ -232,7 +289,11 @@ class FlexFloat:
232
289
  return f"{sign_str}{normalized_mantissa:.5f}e{decimal_exponent:+03d}"
233
290
 
234
291
  def __neg__(self) -> FlexFloat:
235
- """Negate the FlexFloat instance."""
292
+ """Negates the FlexFloat instance.
293
+
294
+ Returns:
295
+ FlexFloat: A new FlexFloat instance with the sign flipped.
296
+ """
236
297
  return FlexFloat(
237
298
  sign=not self.sign,
238
299
  exponent=self.exponent.copy(),
@@ -241,11 +302,12 @@ class FlexFloat:
241
302
 
242
303
  @staticmethod
243
304
  def _grow_exponent(exponent: int, exponent_length: int) -> int:
244
- """Grow the exponent if it exceeds the maximum value for the current length.
305
+ """Grows the exponent if it exceeds the maximum value for the current length.
245
306
 
246
307
  Args:
247
308
  exponent (int): The current exponent value.
248
309
  exponent_length (int): The current length of the exponent in bits.
310
+
249
311
  Returns:
250
312
  int: The new exponent length if it needs to be grown, otherwise the same
251
313
  length.
@@ -262,12 +324,16 @@ class FlexFloat:
262
324
  return exponent_length
263
325
 
264
326
  def __add__(self, other: FlexFloat | Number) -> FlexFloat:
265
- """Add two FlexFloat instances together.
327
+ """Adds two FlexFloat instances together.
266
328
 
267
329
  Args:
268
- other (FlexFloat | float | int): The other FlexFloat instance to add.
330
+ other (FlexFloat | Number): The other FlexFloat instance to add.
331
+
269
332
  Returns:
270
333
  FlexFloat: A new FlexFloat instance representing the sum.
334
+
335
+ Raises:
336
+ TypeError: If other is not a FlexFloat or numeric type.
271
337
  """
272
338
  if isinstance(other, Number):
273
339
  other = FlexFloat.from_float(other)
@@ -309,9 +375,9 @@ class FlexFloat:
309
375
  exponent_self = self.exponent.to_signed_int() + 1
310
376
  exponent_other = other.exponent.to_signed_int() + 1
311
377
 
312
- # Step 2: Prepend leading 1 to form the mantissa
313
- mantissa_self = [True] + self.fraction
314
- mantissa_other = [True] + other.fraction
378
+ # Step 2: Append the implicit leading 1 to form the mantissa
379
+ mantissa_self = self.fraction + [True]
380
+ mantissa_other = other.fraction + [True]
315
381
 
316
382
  # Step 3: Compare exponents (self is always larger or equal)
317
383
  if exponent_self < exponent_other:
@@ -332,9 +398,10 @@ class FlexFloat:
332
398
  f"got {len(mantissa_other)} bits."
333
399
  )
334
400
 
335
- mantissa_result = BitArray.zeros(53) # 1 leading bit + 52 fraction bits
401
+ # 1 leading bit + 52 fraction bits
402
+ mantissa_result = self._bitarray_implementation.zeros(53)
336
403
  carry = False
337
- for i in range(52, -1, -1):
404
+ for i in range(53):
338
405
  total = mantissa_self[i] + mantissa_other[i] + carry
339
406
  mantissa_result[i] = total % 2 == 1
340
407
  carry = total > 1
@@ -352,20 +419,26 @@ class FlexFloat:
352
419
  exponent_self - (1 << (exp_result_length - 1)) < 2
353
420
  ), "Exponent growth should not exceed 1 bit."
354
421
 
355
- exponent_result = BitArray.from_signed_int(exponent_self - 1, exp_result_length)
422
+ exponent_result = self._bitarray_implementation.from_signed_int(
423
+ exponent_self - 1, exp_result_length
424
+ )
356
425
  return FlexFloat(
357
426
  sign=self.sign,
358
427
  exponent=exponent_result,
359
- fraction=mantissa_result[1:], # Exclude leading bit
428
+ fraction=mantissa_result[:-1], # Exclude leading bit
360
429
  )
361
430
 
362
431
  def __sub__(self, other: FlexFloat | Number) -> FlexFloat:
363
- """Subtract one FlexFloat instance from another.
432
+ """Subtracts one FlexFloat instance from another.
364
433
 
365
434
  Args:
366
- other (FlexFloat | float | int): The FlexFloat instance to subtract.
435
+ other (FlexFloat | Number): The FlexFloat instance to subtract.
436
+
367
437
  Returns:
368
438
  FlexFloat: A new FlexFloat instance representing the difference.
439
+
440
+ Raises:
441
+ TypeError: If other is not a FlexFloat or numeric type.
369
442
  """
370
443
  if isinstance(other, Number):
371
444
  other = FlexFloat.from_float(other)
@@ -412,9 +485,9 @@ class FlexFloat:
412
485
  exponent_self = self.exponent.to_signed_int() + 1
413
486
  exponent_other = other.exponent.to_signed_int() + 1
414
487
 
415
- # Step 2: Prepend leading 1 to form the mantissa
416
- mantissa_self = [True] + self.fraction
417
- mantissa_other = [True] + other.fraction
488
+ # Step 2: Append the implicit leading 1 to form the mantissa
489
+ mantissa_self = self.fraction + [True]
490
+ mantissa_other = other.fraction + [True]
418
491
 
419
492
  # Step 3: Align mantissas by shifting the smaller exponent
420
493
  result_sign = self.sign
@@ -450,9 +523,9 @@ class FlexFloat:
450
523
  f"got {len(smaller_mantissa)} bits."
451
524
  )
452
525
 
453
- mantissa_result = BitArray.zeros(53)
526
+ mantissa_result = self._bitarray_implementation.zeros(53)
454
527
  borrow = False
455
- for i in range(52, -1, -1):
528
+ for i in range(53):
456
529
  diff = int(larger_mantissa[i]) - int(smaller_mantissa[i]) - int(borrow)
457
530
 
458
531
  mantissa_result[i] = diff % 2 == 1
@@ -463,7 +536,8 @@ class FlexFloat:
463
536
  # Step 6: Normalize mantissa and adjust exponent if necessary
464
537
  # Find the first 1 bit (leading bit might have been canceled out)
465
538
  leading_zero_count = next(
466
- (i for i, bit in enumerate(mantissa_result) if bit), len(mantissa_result)
539
+ (i for i, bit in enumerate(reversed(mantissa_result)) if bit),
540
+ len(mantissa_result),
467
541
  )
468
542
 
469
543
  # Handle case where result becomes zero or denormalized
@@ -478,21 +552,27 @@ class FlexFloat:
478
552
  # Step 7: Grow exponent if necessary (handle underflow)
479
553
  exp_result_length = self._grow_exponent(result_exponent, len(self.exponent))
480
554
 
481
- exp_result = BitArray.from_signed_int(result_exponent - 1, exp_result_length)
555
+ exp_result = self._bitarray_implementation.from_signed_int(
556
+ result_exponent - 1, exp_result_length
557
+ )
482
558
 
483
559
  return FlexFloat(
484
560
  sign=result_sign,
485
561
  exponent=exp_result,
486
- fraction=mantissa_result[1:], # Exclude leading bit
562
+ fraction=mantissa_result[:-1], # Exclude leading bit
487
563
  )
488
564
 
489
565
  def __mul__(self, other: FlexFloat | Number) -> FlexFloat:
490
- """Multiply two FlexFloat instances together.
566
+ """Multiplies two FlexFloat instances together.
491
567
 
492
568
  Args:
493
- other (FlexFloat | float | int): The other FlexFloat instance to multiply.
569
+ other (FlexFloat | Number): The other FlexFloat instance to multiply.
570
+
494
571
  Returns:
495
572
  FlexFloat: A new FlexFloat instance representing the product.
573
+
574
+ Raises:
575
+ TypeError: If other is not a FlexFloat or numeric type.
496
576
  """
497
577
  if isinstance(other, Number):
498
578
  other = FlexFloat.from_float(other)
@@ -536,9 +616,9 @@ class FlexFloat:
536
616
  result_exponent = exponent_self + exponent_other
537
617
 
538
618
  # Step 4: Multiply mantissas
539
- # Prepend leading 1 to form the mantissa (1.fraction)
540
- mantissa_self = [True] + self.fraction
541
- mantissa_other = [True] + other.fraction
619
+ # Append the implicit leading 1 to form the mantissa
620
+ mantissa_self = self.fraction + [True]
621
+ mantissa_other = other.fraction + [True]
542
622
 
543
623
  # Convert mantissas to integers for multiplication
544
624
  mantissa_self_int = mantissa_self.to_int()
@@ -552,8 +632,8 @@ class FlexFloat:
552
632
  if product == 0:
553
633
  return FlexFloat.zero()
554
634
 
555
- product_bits = BitArray.zeros(106)
556
- for i in range(105, -1, -1):
635
+ product_bits = self._bitarray_implementation.zeros(106)
636
+ for i in range(106):
557
637
  product_bits[i] = product & 1 == 1
558
638
  product >>= 1
559
639
  if product <= 0:
@@ -561,22 +641,28 @@ class FlexFloat:
561
641
 
562
642
  # Step 5: Normalize mantissa and adjust exponent if necessary
563
643
  # Find the position of the most significant bit
564
- msb_position = next((i for i, bit in enumerate(product_bits) if bit), None)
644
+ msb_position = next(
645
+ (i for i, bit in enumerate(reversed(product_bits)) if bit), None
646
+ )
565
647
 
566
648
  assert msb_position is not None, "Product should not be zero here."
567
649
 
568
- # The mantissa multiplication gives us a result with 2 integer bits
650
+ # The mantissa multiplication gives us a result with a 2 integer bits
569
651
  # We need to normalize to have exactly 1 integer bit
570
652
  # If MSB is at position 0, we have a 2-bit integer part (11.xxxxx)
571
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
572
655
  if msb_position == 0:
573
656
  result_exponent += 1
574
- normalized_mantissa = product_bits[msb_position : msb_position + 53]
657
+
658
+ # Extract the normalized mantissa
659
+ lsb_position = 53 - msb_position
660
+ normalized_mantissa = product_bits[lsb_position : lsb_position + 53]
575
661
 
576
662
  # Pad with zeros if we don't have enough bits
577
663
  missing_bits = 53 - len(normalized_mantissa)
578
664
  if missing_bits > 0:
579
- normalized_mantissa += [False] * missing_bits
665
+ normalized_mantissa = normalized_mantissa.shift(-missing_bits, fill=False)
580
666
 
581
667
  # Step 6: Grow exponent if necessary to accommodate the result
582
668
  exp_result_length = max(len(self.exponent), len(other.exponent))
@@ -584,31 +670,38 @@ class FlexFloat:
584
670
  # Check if we need to grow the exponent to accommodate the result
585
671
  exp_result_length = self._grow_exponent(result_exponent, exp_result_length)
586
672
 
587
- exp_result = BitArray.from_signed_int(result_exponent - 1, exp_result_length)
673
+ exp_result = self._bitarray_implementation.from_signed_int(
674
+ result_exponent - 1, exp_result_length
675
+ )
588
676
 
589
677
  return FlexFloat(
590
678
  sign=result_sign,
591
679
  exponent=exp_result,
592
- fraction=normalized_mantissa[1:], # Exclude leading bit
680
+ fraction=normalized_mantissa[:-1], # Exclude leading bit
593
681
  )
594
682
 
595
683
  def __rmul__(self, other: Number) -> FlexFloat:
596
684
  """Right-hand multiplication for Number types.
597
685
 
598
686
  Args:
599
- other (float | int): The number to multiply with this FlexFloat.
687
+ other (Number): The number to multiply with this FlexFloat.
688
+
600
689
  Returns:
601
690
  FlexFloat: A new FlexFloat instance representing the product.
602
691
  """
603
- return self * other
692
+ return self * FlexFloat.from_float(other)
604
693
 
605
694
  def __truediv__(self, other: FlexFloat | Number) -> FlexFloat:
606
- """Divide this FlexFloat by another FlexFloat or number.
695
+ """Divides this FlexFloat by another FlexFloat or number.
607
696
 
608
697
  Args:
609
- other (FlexFloat | float | int): The divisor.
698
+ other (FlexFloat | Number): The divisor.
699
+
610
700
  Returns:
611
701
  FlexFloat: A new FlexFloat instance representing the quotient.
702
+
703
+ Raises:
704
+ TypeError: If other is not a FlexFloat or numeric type.
612
705
  """
613
706
  if isinstance(other, Number):
614
707
  other = FlexFloat.from_float(other)
@@ -661,9 +754,9 @@ class FlexFloat:
661
754
  result_exponent = exponent_self - exponent_other
662
755
 
663
756
  # Step 4: Divide mantissas
664
- # Prepend leading 1 to form the mantissa (1.fraction)
665
- mantissa_self = [True] + self.fraction
666
- mantissa_other = [True] + other.fraction
757
+ # Append the implicit leading 1 to form the mantissa
758
+ mantissa_self = self.fraction + [True]
759
+ mantissa_other = other.fraction + [True]
667
760
 
668
761
  # Convert mantissas to integers for division
669
762
  mantissa_self_int = mantissa_self.to_int()
@@ -682,26 +775,32 @@ class FlexFloat:
682
775
  return FlexFloat.zero()
683
776
 
684
777
  # Convert quotient to BitArray for easier bit manipulation
685
- quotient_bitarray = BitArray.zeros(64) # Use a fixed size for consistency
778
+ # Use a fixed size for consistency
779
+ quotient_bitarray = self._bitarray_implementation.zeros(64)
686
780
  temp_quotient = quotient
687
- bit_pos = 63
688
- while temp_quotient > 0 and bit_pos >= 0:
781
+ bit_pos = 0
782
+ while temp_quotient > 0 and bit_pos < 64:
689
783
  quotient_bitarray[bit_pos] = (temp_quotient & 1) == 1
690
784
  temp_quotient >>= 1
691
- bit_pos -= 1
785
+ bit_pos += 1
692
786
 
693
787
  # Step 5: Normalize mantissa and adjust exponent if necessary
694
788
  # Find the position of the most significant bit (first 1)
695
- msb_pos = next((i for i, bit in enumerate(quotient_bitarray) if bit), None)
789
+ msb_pos = next(
790
+ (i for i, bit in enumerate(reversed(quotient_bitarray)) if bit), None
791
+ )
696
792
 
697
793
  if msb_pos is None:
698
794
  return FlexFloat.zero()
699
795
 
700
796
  # Extract exactly 53 bits starting from the MSB (1 integer + 52 fraction)
701
- normalized_mantissa = quotient_bitarray[msb_pos : msb_pos + 53]
702
- normalized_mantissa = normalized_mantissa.shift(
703
- 53 - len(normalized_mantissa), fill=False
704
- )
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)
705
804
 
706
805
  # Step 6: Grow exponent if necessary to accommodate the result
707
806
  exp_result_length = max(len(self.exponent), len(other.exponent))
@@ -709,20 +808,309 @@ class FlexFloat:
709
808
  # Check if we need to grow the exponent to accommodate the result
710
809
  exp_result_length = self._grow_exponent(result_exponent, exp_result_length)
711
810
 
712
- exp_result = BitArray.from_signed_int(result_exponent - 1, exp_result_length)
811
+ exp_result = self._bitarray_implementation.from_signed_int(
812
+ result_exponent - 1, exp_result_length
813
+ )
713
814
 
714
815
  return FlexFloat(
715
816
  sign=result_sign,
716
817
  exponent=exp_result,
717
- fraction=normalized_mantissa[1:], # Exclude leading bit
818
+ fraction=normalized_mantissa[:-1], # Exclude leading bit
718
819
  )
719
820
 
720
821
  def __rtruediv__(self, other: Number) -> FlexFloat:
721
822
  """Right-hand division for Number types.
722
823
 
723
824
  Args:
724
- other (float | int): The number to divide by this FlexFloat.
825
+ other (Number): The number to divide by this FlexFloat.
826
+
725
827
  Returns:
726
828
  FlexFloat: A new FlexFloat instance representing the quotient.
727
829
  """
728
830
  return FlexFloat.from_float(other) / self
831
+
832
+ def __abs__(self) -> FlexFloat:
833
+ """Returns the absolute value of the FlexFloat instance.
834
+
835
+ Returns:
836
+ FlexFloat: A new FlexFloat instance with the same exponent and fraction, but
837
+ with the sign set to False (positive).
838
+ """
839
+ return FlexFloat(
840
+ sign=False,
841
+ exponent=self.exponent.copy(),
842
+ fraction=self.fraction.copy(),
843
+ )
844
+
845
+ def abs(self) -> FlexFloat:
846
+ """Calculates the absolute value of the FlexFloat instance.
847
+
848
+ Returns:
849
+ FlexFloat: A new FlexFloat instance with the same exponent and fraction, but
850
+ with the sign set to False (positive).
851
+ """
852
+ return abs(self)
853
+
854
+ def exp(self) -> FlexFloat:
855
+ """Calculates the exponential of the FlexFloat instance.
856
+
857
+ Returns:
858
+ FlexFloat: A new FlexFloat instance representing e raised to the power of
859
+ this instance.
860
+ """
861
+ # Use Python's math.exp for the base calculation
862
+ return FlexFloat.e**self
863
+
864
+ def __pow__(self, other: FlexFloat | Number) -> FlexFloat:
865
+ """Raises this FlexFloat to the power of another FlexFloat or number.
866
+
867
+ Args:
868
+ other (FlexFloat | Number): The exponent.
869
+
870
+ Returns:
871
+ FlexFloat: A new FlexFloat instance representing the power.
872
+
873
+ Raises:
874
+ TypeError: If other is not a FlexFloat or numeric type.
875
+ """
876
+ if isinstance(other, Number):
877
+ other = FlexFloat.from_float(other)
878
+ if not isinstance(other, FlexFloat):
879
+ raise TypeError("Can only raise FlexFloat instances to a power.")
880
+
881
+ # OBJECTIVE: Compute self^other using the identity: a^b = exp(b * ln(a))
882
+ # For most cases, we use logarithmic computation for generality
883
+ # Handle special cases first for performance and correctness
884
+ #
885
+ # Steps:
886
+ # 0. Handle special cases (NaN, infinity, zero, one)
887
+ # 1. Handle negative base with fractional exponent (returns NaN)
888
+ # 2. For general case: compute ln(|base|) * exponent
889
+ # 3. Compute exp(result) to get the final power
890
+ # 4. Handle sign for negative base with integer exponent
891
+ # 5. Return new FlexFloat instance
892
+
893
+ # Step 0: Handle special cases
894
+ if self.is_nan() or other.is_nan():
895
+ return FlexFloat.nan()
896
+
897
+ # Zero base cases
898
+ if self.is_zero():
899
+ if other.is_zero():
900
+ return FlexFloat.from_float(1.0) # 0^0 = 1 (by convention)
901
+ if other.sign: # negative exponent
902
+ return FlexFloat.infinity(sign=False) # 0^(-n) = +infinity
903
+ return FlexFloat.zero() # 0^n = 0 for positive n
904
+
905
+ # One base: 1^anything = 1
906
+ if (
907
+ not self.sign
908
+ and self.exponent.to_signed_int() == -1
909
+ and not any(self.fraction)
910
+ ):
911
+ # Check if self is exactly 1.0 (exponent = -1, fraction = 0)
912
+ return FlexFloat.from_float(1.0)
913
+
914
+ # Zero exponent: anything^0 = 1 (except 0^0 handled above)
915
+ if other.is_zero():
916
+ return FlexFloat.from_float(1.0)
917
+
918
+ # One exponent: anything^1 = anything
919
+ if (
920
+ not other.sign
921
+ and other.exponent.to_signed_int() == -1
922
+ and not any(other.fraction)
923
+ ):
924
+ # Check if other is exactly 1.0 (exponent = -1, fraction = 0)
925
+ return self.copy()
926
+
927
+ # Infinity cases
928
+ if self.is_infinity():
929
+ if other.is_zero():
930
+ return FlexFloat.from_float(1.0) # inf^0 = 1
931
+ if other.sign: # negative exponent
932
+ return FlexFloat.zero() # inf^(-n) = 0
933
+ # inf^n = inf, but need to handle sign for negative infinity
934
+ if not self.sign: # positive infinity
935
+ return FlexFloat.infinity(sign=False) # (+inf)^n = +inf
936
+
937
+ # Check if exponent is an integer for sign determination
938
+ try:
939
+ exp_float = other.to_float()
940
+ if exp_float == int(exp_float): # integer exponent
941
+ result_sign = int(exp_float) % 2 == 1 # odd = negative
942
+ else:
943
+ return FlexFloat.nan() # (-inf)^(non-integer) = NaN
944
+ except (ValueError, OverflowError):
945
+ # For very large exponents, assume positive result
946
+ result_sign = False
947
+ return FlexFloat.infinity(sign=result_sign)
948
+
949
+ if other.is_infinity():
950
+ try:
951
+ base_abs = abs(self.to_float())
952
+ if base_abs == 1.0:
953
+ return FlexFloat.from_float(1.0) # 1^inf = 1
954
+ if base_abs > 1.0:
955
+ return (
956
+ FlexFloat.infinity(sign=False)
957
+ if not other.sign
958
+ else FlexFloat.zero()
959
+ )
960
+ # base_abs < 1.0
961
+ return (
962
+ FlexFloat.zero()
963
+ if not other.sign
964
+ else FlexFloat.infinity(sign=False)
965
+ )
966
+ except (ValueError, OverflowError):
967
+ # For very large/small bases, use log-based approach
968
+ pass
969
+
970
+ # Step 1: Handle negative base with fractional exponent
971
+ if self.sign:
972
+ try:
973
+ # Check if exponent is an integer
974
+ exp_float = other.to_float()
975
+ if exp_float != int(exp_float):
976
+ return FlexFloat.nan() # (-base)^(non-integer) = NaN
977
+ is_odd_exponent = int(exp_float) % 2 == 1
978
+ except (ValueError, OverflowError):
979
+ # For very large exponents, we can't easily determine if integer
980
+ # Conservative approach: return NaN for negative base
981
+ return FlexFloat.nan()
982
+ else:
983
+ is_odd_exponent = False
984
+
985
+ # Step 2-3: General case using a^b = exp(b * ln(a))
986
+ # We need to compute ln(|self|) * other, then exp of that result
987
+
988
+ # For the logarithmic approach, we work with the absolute value
989
+ abs_base = self.abs()
990
+
991
+ # Use the mathematical identity: a^b = exp(b * ln(a))
992
+ # However, since we don't have ln implemented, we'll use Python's math
993
+ # for the core calculation and then convert back to FlexFloat
994
+ try:
995
+ # Convert to float for the mathematical computation
996
+ base_float = abs_base.to_float()
997
+ exp_float = other.to_float()
998
+
999
+ # For very small results that would underflow in float arithmetic,
1000
+ # we need to use extended precision
1001
+ log_result_estimate = exp_float * math.log(base_float)
1002
+
1003
+ # Check if the result would be too small for standard float
1004
+ if log_result_estimate < -700: # This would underflow to 0 in float
1005
+ # Use extended precision approach
1006
+ # Estimate the required exponent bits for the very small result
1007
+ estimated_exp = int(log_result_estimate / LOG10_2)
1008
+ required_exp_bits = max(11, abs(estimated_exp).bit_length() + 2)
1009
+
1010
+ # Create a small result with extended exponent
1011
+ small_exp = self._bitarray_implementation.from_signed_int(
1012
+ estimated_exp, required_exp_bits
1013
+ )
1014
+ # Use a normalized mantissa (leading 1 + some fraction bits)
1015
+ result = FlexFloat(
1016
+ sign=False,
1017
+ exponent=small_exp,
1018
+ fraction=self._bitarray_implementation.ones(52),
1019
+ )
1020
+ else:
1021
+ # Compute the power using Python's built-in pow
1022
+ result_float = pow(base_float, exp_float)
1023
+
1024
+ # Handle potential overflow/underflow
1025
+ if math.isinf(result_float):
1026
+ return FlexFloat.infinity(sign=False)
1027
+ if result_float == 0.0:
1028
+ # Even if Python returns 0, we might want extended precision
1029
+ if log_result_estimate < -300: # Very small but not quite underflow
1030
+ estimated_exp = int(log_result_estimate / LOG10_2)
1031
+ required_exp_bits = max(11, abs(estimated_exp).bit_length() + 2)
1032
+ small_exp = self._bitarray_implementation.from_signed_int(
1033
+ estimated_exp, required_exp_bits
1034
+ )
1035
+ result = FlexFloat(
1036
+ sign=False,
1037
+ exponent=small_exp,
1038
+ fraction=self._bitarray_implementation.ones(52),
1039
+ )
1040
+ else:
1041
+ return FlexFloat.zero()
1042
+ if math.isnan(result_float):
1043
+ return FlexFloat.nan()
1044
+
1045
+ # Convert result back to FlexFloat
1046
+ result = FlexFloat.from_float(result_float)
1047
+
1048
+ except (ValueError, OverflowError):
1049
+ # If float computation overflows, use extended precision approach
1050
+ # This is a fallback for when the result is too large for float
1051
+
1052
+ # For very large results, we estimate the exponent growth needed
1053
+ try:
1054
+ # Estimate log(result) = exponent * log(base)
1055
+ log_base = (
1056
+ math.log(abs(self.to_float()))
1057
+ if not self.is_zero()
1058
+ else -float("inf")
1059
+ )
1060
+ exp_float = other.to_float()
1061
+ estimated_log_result = exp_float * log_base
1062
+
1063
+ # Estimate the required exponent bits
1064
+ estimated_exp_float = estimated_log_result / LOG10_2
1065
+ required_exp_bits = max(
1066
+ 11, int(abs(estimated_exp_float)).bit_length() + 2
1067
+ )
1068
+
1069
+ # Create a result with extended exponent
1070
+ # This is a simplified approach - a full implementation would
1071
+ # use arbitrary precision arithmetic
1072
+ if estimated_exp_float > 0:
1073
+ # Very large positive result
1074
+ large_exp = self._bitarray_implementation.from_signed_int(
1075
+ int(estimated_exp_float), required_exp_bits
1076
+ )
1077
+ result = FlexFloat(
1078
+ sign=False,
1079
+ exponent=large_exp,
1080
+ fraction=self._bitarray_implementation.ones(52),
1081
+ )
1082
+ else:
1083
+ # Very small positive result
1084
+ small_exp = self._bitarray_implementation.from_signed_int(
1085
+ int(estimated_exp_float), required_exp_bits
1086
+ )
1087
+ result = FlexFloat(
1088
+ sign=False,
1089
+ exponent=small_exp,
1090
+ fraction=self._bitarray_implementation.ones(52),
1091
+ )
1092
+
1093
+ except (ValueError, OverflowError, ZeroDivisionError):
1094
+ # Ultimate fallback
1095
+ return FlexFloat.nan()
1096
+
1097
+ # Step 4: Handle sign for negative base with integer exponent
1098
+ if self.sign and is_odd_exponent:
1099
+ result.sign = True
1100
+
1101
+ return result
1102
+
1103
+ def __rpow__(self, base: Number) -> FlexFloat:
1104
+ """Right-hand power operation for Number types.
1105
+
1106
+ Args:
1107
+ base (Number): The base to raise to the power of this FlexFloat.
1108
+
1109
+ Returns:
1110
+ FlexFloat: A new FlexFloat instance representing the power.
1111
+ """
1112
+ return FlexFloat.from_float(base) ** self
1113
+
1114
+
1115
+ # Initialize class variable after class definition
1116
+ FlexFloat.e = FlexFloat.from_float(math.e)