Skip to content

Commit

Permalink
Let users pass in Decimals & add precision config key
Browse files Browse the repository at this point in the history
  • Loading branch information
Splines committed Mar 19, 2024
1 parent 0951c3e commit 93ddb30
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 51 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
//////////////////////////////////////
"cSpell.words": [
"Commandifier",
"getcontext",
"github",
"ifthen",
"ifthenelse",
Expand All @@ -70,12 +71,14 @@
"newcommand",
"normalsize",
"pipenv",
"prec",
"pydantic",
"pylint",
"pytest",
"resultwizard",
"scriptsize",
"scriptstyle",
"setcontext",
"sigfigs",
"siunitx",
"Stringifier",
Expand Down
10 changes: 10 additions & 0 deletions src/api/config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import decimal
from typing import Union, cast

from dataclasses import dataclass
Expand Down Expand Up @@ -33,6 +34,9 @@ class Config:
siunitx_fallback (bool): Whether to use a fallback logic such that LaTeX
commands still work with an older version of siunitx. See
the docs for more information: TODO.
precision (int): The precision to use for the decimal module. Defaults to
40 in ResultsWizard. You may have to increase this if you get the error
"Your precision is set too low".
"""

sigfigs: int
Expand All @@ -45,6 +49,7 @@ class Config:
sigfigs_fallback: int
decimal_places_fallback: int
siunitx_fallback: bool
precision: int

def to_stringifier_config(self) -> StringifierConfig:
return StringifierConfig(
Expand Down Expand Up @@ -100,9 +105,13 @@ def config_init(
sigfigs_fallback: int = 2,
decimal_places_fallback: int = -1, # -1: "per default use sigfigs as fallback instead"
siunitx_fallback: bool = False,
precision: int = 100,
) -> None:
global configuration # pylint: disable=global-statement

decimal.DefaultContext.prec = precision
decimal.setcontext(decimal.DefaultContext)

configuration = Config(
sigfigs,
decimal_places,
Expand All @@ -114,6 +123,7 @@ def config_init(
sigfigs_fallback,
decimal_places_fallback,
siunitx_fallback,
precision,
)

_check_config()
Expand Down
26 changes: 11 additions & 15 deletions src/api/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,15 @@ def parse_decimal_places(decimal_places: Union[int, None]) -> Union[int, None]:
return decimal_places


def parse_value(value: Union[float, int, str]) -> Value:
def parse_value(value: Union[float, int, str, Decimal]) -> Value:
"""Converts the value to a _Value object."""
if not isinstance(value, (float, int, str)):
raise TypeError(f"`value` must be a float, int or string, not {type(value)}")
if not isinstance(value, (float, int, str, Decimal)):
raise TypeError(f"`value` must be a float, int, Decimal or string, not {type(value)}")

if isinstance(value, str):
check_if_number_string(value)
return parse_exact_value(value)

if isinstance(value, int):
value = float(value)

return Value(Decimal(value))


Expand All @@ -161,28 +158,27 @@ def parse_uncertainties(
float,
int,
str,
Tuple[Union[float, int, str], str],
List[Union[float, str, Tuple[Union[float, int, str], str]]],
Decimal,
Tuple[Union[float, int, str, Decimal], str],
List[Union[float, int, str, Decimal, Tuple[Union[float, int, str, Decimal], str]]],
]
) -> List[Uncertainty]:
"""Converts the uncertainties to a list of _Uncertainty objects."""
uncertainties_res = []

# no list, but a single value was given
if isinstance(uncertainties, (float, int, str, Tuple)):
if isinstance(uncertainties, (float, int, str, Decimal, Tuple)):
uncertainties = [uncertainties]

assert isinstance(uncertainties, List)

for uncert in uncertainties:
if isinstance(uncert, (float, int, str)):
if isinstance(uncert, (float, int, str, Decimal)):
uncertainties_res.append(Uncertainty(_parse_uncertainty_value(uncert)))

elif isinstance(uncert, Tuple):
if not isinstance(uncert[0], (float, int, str)):
if not isinstance(uncert[0], (float, int, str, Decimal)):
raise TypeError(
"First argument of uncertainty-tuple must be a float,"
+ f" int or a string, not {type(uncert[0])}"
+ f" int, Decimal or a string, not {type(uncert[0])}"
)
uncertainties_res.append(
Uncertainty(_parse_uncertainty_value(uncert[0]), parse_name(uncert[1]))
Expand All @@ -196,7 +192,7 @@ def parse_uncertainties(
return uncertainties_res


def _parse_uncertainty_value(value: Union[float, int, str]) -> Value:
def _parse_uncertainty_value(value: Union[float, int, str, Decimal]) -> Value:
"""Parses the value of an uncertainty."""

if isinstance(value, str):
Expand Down
21 changes: 12 additions & 9 deletions src/api/res.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from decimal import Decimal
from typing import Union, List, Tuple
from plum import dispatch, overload

Expand All @@ -17,7 +18,7 @@
@overload
def res(
name: str,
value: Union[float, int, str],
value: Union[float, int, str, Decimal],
unit: str = "",
sigfigs: Union[int, None] = None,
decimal_places: Union[int, None] = None,
Expand All @@ -28,12 +29,13 @@ def res(
@overload
def res(
name: str,
value: Union[float, int, str],
value: Union[float, int, str, Decimal],
uncert: Union[
float,
str,
Tuple[Union[float, int, str], str],
List[Union[float, int, str, Tuple[Union[float, int, str], str]]],
Decimal,
Tuple[Union[float, int, str, Decimal], str],
List[Union[float, int, str, Decimal, Tuple[Union[float, int, str, Decimal], str]]],
None,
] = None,
sigfigs: Union[int, None] = None,
Expand All @@ -45,7 +47,7 @@ def res(
@overload
def res(
name: str,
value: Union[float, int, str],
value: Union[float, int, str, Decimal],
sigfigs: Union[int, None] = None,
decimal_places: Union[int, None] = None,
) -> PrintableResult:
Expand All @@ -56,7 +58,7 @@ def res(
# pylint: disable=too-many-arguments
def res(
name: str,
value: Union[float, int, str],
value: Union[float, int, str, Decimal],
sys: float,
stat: float,
unit: str = "",
Expand All @@ -70,12 +72,13 @@ def res(
# pylint: disable=too-many-arguments
def res(
name: str,
value: Union[float, int, str],
value: Union[float, int, str, Decimal],
uncert: Union[
float,
str,
Tuple[Union[float, int, str], str],
List[Union[float, int, str, Tuple[Union[float, int, str], str]]],
Decimal,
Tuple[Union[float, int, str, Decimal], str],
List[Union[float, int, str, Decimal, Tuple[Union[float, int, str, Decimal], str]]],
None,
] = None,
unit: str = "",
Expand Down
11 changes: 8 additions & 3 deletions src/application/helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import math
import decimal
from decimal import Decimal

_NUMBER_TO_WORD = {
Expand Down Expand Up @@ -45,9 +46,13 @@ def get_first_digit(cls, value: Decimal) -> int:

@classmethod
def round_to_n_decimal_places(cls, value: Decimal, n: int) -> str:
decimal_compare = f"1.{'0' * abs(n)}"
decimal = value.quantize(Decimal(decimal_compare))
return str(decimal)
try:
decimal_value = value.quantize(Decimal(f"1.{'0' * abs(n)}"))
return f"{decimal_value:.{abs(n)}f}"
except decimal.InvalidOperation as exc:
raise ValueError(
"Your precision is set too low to be able to process the given value without any loss of precision. Set a higher precision via: `wiz.config_init(precision=<a-high-enough-number>)`."
) from exc

@classmethod
def number_to_word(cls, number: int) -> str:
Expand Down
35 changes: 11 additions & 24 deletions tests/playground.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# (e.g. the site-packages directory)."
# From: https://setuptools.pypa.io/en/latest/userguide/quickstart.html#development-mode


from decimal import Decimal
import resultwizard as wiz

print("#############################")
Expand All @@ -17,7 +17,6 @@
wiz.config_init(
print_auto=True,
export_auto_to="results-immediate.tex",
decimal_places=3,
siunitx_fallback=False,
)
# wiz.config(sigfigs=2)
Expand All @@ -32,33 +31,21 @@
# wiz.res("", 42.0).print()
# -> Error: "name must not be empty"

wiz.res("a911", 1.0, r"\mm\s\per\N\kg")
# a: 1.0 \mm

wiz.res("1 b", 1.0, 0.01, r"\per\mm\cubed").print()
# b: (1.0 ± 0.01) \mm

wiz.config(decimal_places=-1, sigfigs_fallback=3)

wiz.res("c big", 1.0, (0.01, "systematic"), r"\mm").print()
# c: (1.0 ± 0.01 systematic) \mm
wiz.res("a911", 1.05, r"\mm\s\per\N\kg")
# wiz.res("a911", "1.052", 0.25, r"\mm\s\per\N\kg")

wiz.res(
"d", 1.0e10, [(0.01e10, "systematic"), (0.0294999999e10, "stat")], r"\mm\per\second\squared"
).print()
# d: (1.0 ± 0.01 systematic ± 0.02 stat) \mm
wiz.res("1 b", 1.0, 0.01, r"\per\mm\cubed")

wiz.res("e", "1.0", r"\mm").print()
# e: 1.0 \mm

wiz.res("f", "1.0e1", 25e-1).print()
# f: 1.0

wiz.res("g", 42).print()
# wiz.config(decimal_places=-1, sigfigs_fallback=3)

wiz.res("c big", 1.0, (0.01, "systematic"), r"\mm")
wiz.res("d", 1.0e10, [(0.01e10, "systematic"), (0.0294999e10, "stat")], r"\mm\per\second\squared")
wiz.res("e", "1.0", r"\mm")
wiz.res("f", "1.0e1", 25e-1)
wiz.res("g", 42)
wiz.res("h", 42, 13.0, 24.0)
wiz.res("h&", 42, 13.0, 24.0)

wiz.res("i", Decimal("42.0e-30"), Decimal("0.1e-31"), r"\m")

# wiz.res("g", 1.0, sys=0.01, stat=0.02, unit=r"\mm").print()
# g: (1.0 ± 0.01 sys ± 0.02 stat) \mm
Expand Down

0 comments on commit 93ddb30

Please sign in to comment.