Skip to content

Commit

Permalink
cast from & to decimal (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
dpaiton authored Nov 17, 2023
1 parent 2b4ab03 commit 5d78770
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 8 deletions.
15 changes: 11 additions & 4 deletions lib/fixedpointmath/fixedpointmath/fixed_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
from __future__ import annotations

import re
from decimal import Decimal
from typing import Any, Literal, Union, get_args

from . import errors
from .fixed_point_integer_math import FixedPointIntegerMath

OtherTypes = Union[int, bool, float]
OtherTypes = Union[int, bool, float, Decimal]
SpecialValues = Literal["nan", "inf", "-inf"]


Expand All @@ -31,6 +32,7 @@ class FixedPoint:
_scaled_value: int # integer representation of self
_special_value: SpecialValues | None # string representation of self

# pylint: disable=too-many-branches
def __init__(
self,
unscaled_value: OtherTypes | str | FixedPoint | None = None, # use default conversion
Expand Down Expand Up @@ -70,6 +72,8 @@ def __init__(
self._set_int(unscaled_value)
elif isinstance(unscaled_value, str):
self._set_str(unscaled_value)
elif isinstance(unscaled_value, Decimal):
self._set_str(str(unscaled_value))
elif isinstance(unscaled_value, FixedPoint):
self._set_fixedpoint(unscaled_value)
else:
Expand Down Expand Up @@ -116,13 +120,10 @@ def _set_str(self, unscaled_value: str) -> None:
exponent = int(exp_split[1])
else:
exponent = 0

if "." not in mantissa: # input is always assumed to be a float
mantissa += ".0"

# removes underscores; they won't affect `int` cast and will affect `len`
mantissa = mantissa.replace("_", "")

integer, remainder = mantissa.split(".")
is_negative = "-" in integer
if is_negative:
Expand Down Expand Up @@ -228,12 +229,18 @@ def _coerce_other(self, other: OtherTypes | FixedPoint) -> FixedPoint:
return other
if isinstance(other, int):
return FixedPoint(other)
if isinstance(other, Decimal):
return FixedPoint(other)
if isinstance(other, float): # currently don't allow (most) floats
if other == 0.0: # 0 is unambiguous, so we will allow it
return FixedPoint(other)
raise TypeError(f"unsupported operand type(s): {type(other)}")
raise TypeError(f"unsupported operand type(s): {type(other)}")

def to_decimal(self) -> Decimal:
r"""Convert self to a Decimal type"""
return Decimal(str(self))

def __add__(self, other: OtherTypes | FixedPoint) -> FixedPoint:
r"""Enables '+' syntax"""
other = self._coerce_other(other)
Expand Down
2 changes: 1 addition & 1 deletion lib/fixedpointmath/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "fixedpointmath"
version = "0.1.3"
version = "0.1.4"
authors = [
{ name = "Dylan Paiton", email = "dylan@delv.tech" },
{ name = "Mihai Cosma", email = "mihai@delv.tech" },
Expand Down
16 changes: 13 additions & 3 deletions lib/fixedpointmath/tests/test_fp_class.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Tests for the FixedPoint class methods"""
import math
import unittest
from decimal import Decimal

from fixedpointmath import errors
from fixedpointmath import FixedPoint
from fixedpointmath import FixedPoint, errors


class TestFixedPoint(unittest.TestCase):
Expand Down Expand Up @@ -41,6 +41,8 @@ def test_init(self):
assert int(FixedPoint(5)) == 5 # int input directly maps, cast does not rescale
assert float(FixedPoint(5.0)) == 5.0 # scales up on init, then back down on cast to float
assert int(FixedPoint(5)) == float(FixedPoint(5.0))
# decimal == float
assert FixedPoint(Decimal("5.0")) == FixedPoint(5.0)
# bool
assert FixedPoint(True) == FixedPoint(1.0)
assert FixedPoint(False) == FixedPoint(0.0)
Expand Down Expand Up @@ -130,8 +132,16 @@ def test_str_cast(self):
assert str(FixedPoint(True)) == "1.0"
assert str(FixedPoint(False)) == "0.0"

def test_decimal_cast(self):
r"""Test Decimal casting"""
assert FixedPoint("0.15").to_decimal() == Decimal("0.15")
assert float(FixedPoint("0.15").to_decimal()) == 0.15
assert FixedPoint(Decimal("0.15")) == FixedPoint("0.15")
assert int(FixedPoint(Decimal(5))) == 5
assert isinstance(FixedPoint(5.52).to_decimal(), Decimal)

def test_repr(self):
"""Test the repr method"""
r"""Test the repr method"""
# pylint: disable=unnecessary-dunder-call
assert self.INF.__repr__() == 'FixedPoint("inf")'
assert self.NEG_INF.__repr__() == 'FixedPoint("-inf")'
Expand Down

0 comments on commit 5d78770

Please sign in to comment.