Skip to content

Commit bb814df

Browse files
committed
Use correctly contextualized Decimal loader
1 parent 1d2621d commit bb814df

4 files changed

Lines changed: 42 additions & 34 deletions

File tree

tests/numeric.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import unittest
22

3-
from decimal import Decimal
3+
from decimal import Decimal, Overflow
44

55
from tivars.types import *
66

@@ -62,6 +62,19 @@ def test_sig_figs(self):
6262
self.assertEqual(str(TIReal("1.2345678901234e-8")), "1.2345678901234E-8")
6363
self.assertEqual(str(TIReal("1.2345678901234E88")), "1.2345678901234E88")
6464

65+
self.assertEqual(str(TIReal(2.675)), "2.675")
66+
self.assertEqual(str(TIReal(0.1 + 0.2)), "0.3")
67+
68+
self.assertEqual(str(TIReal("0009.5")), "9.5")
69+
self.assertEqual(str(TIReal("000000000000001")), "1")
70+
self.assertEqual(str(TIReal("12345678901234567890")), "1.2345678901234E19")
71+
72+
with self.assertRaises(Overflow):
73+
TIReal(5e300)
74+
75+
with self.assertWarns(UserWarning):
76+
TIReal(1.8e100)
77+
6578

6679
class ComplexTests(unittest.TestCase):
6780
def complex_float_test(self, comp_type, filename, name, real_sign, real_exponent, real_mantissa,

tivars/numeric.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,14 @@
88
from .data import *
99

1010

11-
pi = dec.Decimal("3.1415926535898")
12-
e = dec.Decimal("2.718281828459")
11+
12+
TI_CONTEXT = dec.Context(prec=14, rounding=dec.ROUND_DOWN, Emin=-128, Emax=128, capitals=1)
13+
"""
14+
Decimal context for working with TI floats
15+
"""
16+
17+
pi = TI_CONTEXT.create_decimal("3.1415926535898")
18+
e = TI_CONTEXT.create_decimal("2.718281828459")
1319

1420

1521
def sign(x: int) -> int:
@@ -142,4 +148,4 @@ def set(cls, value: int, *, current: bytes = None, **kwargs) -> bytes:
142148
return bytes(data)
143149

144150

145-
__all__ = ["pi", "e", "sign", "BCD", "LeftNibbleBCD", "RightNibbleBCD"]
151+
__all__ = ["TI_CONTEXT", "pi", "e", "sign", "BCD", "LeftNibbleBCD", "RightNibbleBCD"]

tivars/types/complex.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55

6+
from decimal import InvalidOperation
67
from warnings import warn
78

89
from tivars.data import *
@@ -317,7 +318,7 @@ def load_string(self, string: str, **kwargs):
317318
try:
318319
self.real = self.real_type(parts[0])
319320

320-
except (TypeError, ValueError):
321+
except (InvalidOperation, TypeError, ValueError):
321322
for type_id, entry_type in self._type_ids.items():
322323
if not issubclass(entry_type, RealEntry):
323324
continue
@@ -329,7 +330,7 @@ def load_string(self, string: str, **kwargs):
329330
self.real = self.get_type(type_id=type_id)(parts[0])
330331
break
331332

332-
except (TypeError, ValueError):
333+
except (InvalidOperation, TypeError, ValueError):
333334
continue
334335

335336
else:

tivars/types/real.py

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from decimal import Decimal, localcontext
1010
from fractions import Fraction
11+
from warnings import warn
1112

1213
from tivars.data import *
1314
from tivars.models import *
@@ -171,7 +172,7 @@ def load_float(self, decimal: float):
171172
:param decimal: The float to load
172173
"""
173174

174-
self.load_decimal(Decimal(decimal))
175+
self.load_decimal(Decimal(str(decimal)))
175176

176177
def json_number(self) -> float | str:
177178
"""
@@ -252,36 +253,23 @@ def load_string(self, string: str, **kwargs):
252253
# Normalize string
253254
string = replacer(squash(string).upper(), {"~": "-", "|E": "E"})
254255

255-
if "E" not in string:
256-
string += "E0"
256+
# Let Decimal handle it
257+
self.sign_bit, digits, exponent = TI_CONTEXT.create_decimal(string).as_tuple()
257258

258-
if "." not in string:
259-
string = string.replace("E", ".E")
259+
if not isinstance(exponent, int):
260+
raise OverflowError("cannot create real number from ±inf")
260261

261-
neg = string.startswith("-")
262-
string = string.strip("+-")
262+
elif digits == (0,):
263+
self.mantissa = 0
264+
self.exponent = 0x80
263265

264-
# Obtain integer and decimal parts
265-
number, exponent = string.split("E")
266-
integer, decimal = number.split(".")
267-
integer, decimal = integer or "0", decimal or "0"
268-
269-
if int(integer) == int(decimal) == 0:
270-
self.mantissa, self.exponent, self.sign_bit = 0, 0x80, neg
271-
return
272-
273-
# Adjust exponent to make integer mantissa
274-
exponent = int(exponent or "0")
275-
while not 0 < (value := int(integer)) < 10:
276-
if value == 0:
277-
integer, decimal = decimal[0], decimal[1:]
278-
exponent -= 1
279-
280-
else:
281-
integer, decimal = integer[:-1], integer[-1] + decimal
282-
exponent += 1
266+
else:
267+
self.mantissa = int("".join(map(str, digits)).ljust(14, "0"))
268+
self.exponent = exponent + (len(digits) - 1) + 0x80
283269

284-
self.mantissa, self.exponent, self.sign_bit = int((integer + decimal).ljust(14, "0")[:14]), exponent + 0x80, neg
270+
if self.exponent < -29 or 227 < self.exponent:
271+
warn(f"Values with |exponent| > 99 raise errors when used for arithmetic.",
272+
UserWarning)
285273

286274

287275
class TIUndefinedReal(TIReal, register=True):
@@ -326,7 +314,7 @@ def __format__(self, format_spec: str) -> str:
326314
def load_fraction(self, fraction: Fraction):
327315
with localcontext() as ctx:
328316
ctx.prec = 14
329-
decimal = Decimal(fraction.numerator) / fraction.denominator
317+
decimal = TI_CONTEXT.create_decimal(fraction.numerator) / fraction.denominator
330318

331319
super().load_string(str(decimal))
332320

0 commit comments

Comments
 (0)