sciform 0.36.0__py3-none-any.whl → 0.38.0__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.
- sciform/__init__.py +0 -3
- sciform/api/formatter.py +12 -22
- sciform/api/global_configuration.py +5 -9
- sciform/format_utils/exponents.py +2 -3
- sciform/format_utils/make_strings.py +32 -22
- sciform/format_utils/numbers.py +13 -14
- sciform/format_utils/rounding.py +17 -21
- sciform/formatting/fsml.py +23 -8
- sciform/formatting/number_formatting.py +68 -39
- sciform/options/conversion.py +2 -1
- sciform/options/finalized_options.py +2 -8
- sciform/options/global_options.py +3 -5
- sciform/options/input_options.py +2 -3
- sciform/options/option_types.py +12 -46
- sciform/options/populated_options.py +4 -6
- sciform/options/validation.py +69 -59
- {sciform-0.36.0.dist-info → sciform-0.38.0.dist-info}/METADATA +68 -55
- sciform-0.38.0.dist-info/RECORD +31 -0
- {sciform-0.36.0.dist-info → sciform-0.38.0.dist-info}/WHEEL +1 -1
- sciform-0.36.0.dist-info/RECORD +0 -31
- {sciform-0.36.0.dist-info → sciform-0.38.0.dist-info}/LICENSE +0 -0
- {sciform-0.36.0.dist-info → sciform-0.38.0.dist-info}/top_level.txt +0 -0
sciform/__init__.py
CHANGED
@@ -11,7 +11,6 @@ from sciform.api.global_configuration import (
|
|
11
11
|
)
|
12
12
|
from sciform.api.scinum import SciNum
|
13
13
|
from sciform.options.input_options import InputOptions
|
14
|
-
from sciform.options.option_types import AutoDigits, AutoExpVal
|
15
14
|
from sciform.options.populated_options import PopulatedOptions
|
16
15
|
|
17
16
|
__all__ = [
|
@@ -22,8 +21,6 @@ __all__ = [
|
|
22
21
|
"get_global_options",
|
23
22
|
"reset_global_options",
|
24
23
|
"set_global_options",
|
25
|
-
"AutoDigits",
|
26
|
-
"AutoExpVal",
|
27
24
|
"SciNum",
|
28
25
|
"InputOptions",
|
29
26
|
"PopulatedOptions",
|
sciform/api/formatter.py
CHANGED
@@ -107,7 +107,7 @@ class Formatter:
|
|
107
107
|
>>> print(formatter.populated_options)
|
108
108
|
PopulatedOptions(
|
109
109
|
'exp_mode': 'engineering',
|
110
|
-
'exp_val':
|
110
|
+
'exp_val': 'auto',
|
111
111
|
'round_mode': 'sig_fig',
|
112
112
|
'ndigits': 2,
|
113
113
|
'upper_separator': '',
|
@@ -124,7 +124,6 @@ class Formatter:
|
|
124
124
|
'superscript': True,
|
125
125
|
'nan_inf_exp': False,
|
126
126
|
'paren_uncertainty': False,
|
127
|
-
'pdg_sig_figs': False,
|
128
127
|
'left_pad_matching': False,
|
129
128
|
'paren_uncertainty_trim': True,
|
130
129
|
'pm_whitespace': True,
|
@@ -143,9 +142,9 @@ class Formatter:
|
|
143
142
|
self: Formatter,
|
144
143
|
*,
|
145
144
|
exp_mode: option_types.ExpMode | None = None,
|
146
|
-
exp_val: int |
|
145
|
+
exp_val: int | option_types.ExpVal | None = None,
|
147
146
|
round_mode: option_types.RoundMode | None = None,
|
148
|
-
ndigits: int |
|
147
|
+
ndigits: int | None = None,
|
149
148
|
upper_separator: option_types.UpperSeparators | None = None,
|
150
149
|
decimal_separator: option_types.DecimalSeparators | None = None,
|
151
150
|
lower_separator: option_types.LowerSeparators | None = None,
|
@@ -160,7 +159,6 @@ class Formatter:
|
|
160
159
|
superscript: bool | None = None,
|
161
160
|
nan_inf_exp: bool | None = None,
|
162
161
|
paren_uncertainty: bool | None = None,
|
163
|
-
pdg_sig_figs: bool | None = None,
|
164
162
|
left_pad_matching: bool | None = None,
|
165
163
|
paren_uncertainty_trim: bool | None = None,
|
166
164
|
pm_whitespace: bool | None = None,
|
@@ -174,10 +172,10 @@ class Formatter:
|
|
174
172
|
The following checks are performed when creating a new
|
175
173
|
:class:`Formatter` object:
|
176
174
|
|
177
|
-
* ``
|
175
|
+
* If ``round_mode`` is ``"sig_fig"`` then ``ndigits`` >= 1.
|
178
176
|
* ``exp_val`` must be consistent with the exponent mode. If
|
179
177
|
``exp_val`` is specified (i.e. not ``None``) and ``exp_val``
|
180
|
-
is not ``
|
178
|
+
is not ``"auto"`` then
|
181
179
|
|
182
180
|
* ``exp_val`` must be 0 for fixed point and percent modes
|
183
181
|
* ``exp_val`` must be a multiple of 3 for engineering and
|
@@ -189,7 +187,7 @@ class Formatter:
|
|
189
187
|
* ``decimal_separator`` may be any of ``['.', ',']``
|
190
188
|
* ``lower_separator`` may be any of ``['', ' ', '_']``
|
191
189
|
|
192
|
-
:param exp_mode: Specify the formatting mode.
|
190
|
+
:param exp_mode: Specify the exponent formatting mode.
|
193
191
|
:type exp_mode: ``Literal['fixed_point', 'percent',
|
194
192
|
'scientific', 'engineering', 'engineering_shifted', 'binary',
|
195
193
|
'binary_iec'] | None``
|
@@ -197,16 +195,14 @@ class Formatter:
|
|
197
195
|
chosen. If an integer is specified, the value must be 0 for
|
198
196
|
fixed point and percent modes, an integer multiple of 3 for
|
199
197
|
engineering and engineering shifted modes, and an integer
|
200
|
-
multiple of 10 for binary IEC mode.
|
201
|
-
:type exp_val: ``int |
|
198
|
+
multiple of 10 for binary IEC mode. Can be set to ``"auto"``.
|
199
|
+
:type exp_val: ``int | Literal['auto'] | None``
|
202
200
|
:param round_mode: Indicate how to round numbers during
|
203
201
|
formatting.
|
204
|
-
:type round_mode: ``Literal['sig_fig', 'dec_place'] | None``
|
205
|
-
:param ndigits:
|
206
|
-
decimal
|
207
|
-
|
208
|
-
place rounding.
|
209
|
-
:type ndigits: ``int | type[AutoDigits] | None``
|
202
|
+
:type round_mode: ``Literal['sig_fig', 'dec_place', 'all', 'pdg'] | None``
|
203
|
+
:param ndigits: Specifies how many digits to use for significant figure
|
204
|
+
or digits-past-the-decimal rounding.
|
205
|
+
:type ndigits: ``int | None``
|
210
206
|
:param upper_separator: Separator character to be used to group
|
211
207
|
digits above the decimal symbol.
|
212
208
|
:type upper_separator: ``Literal['', ',', '.', ' ', '_'] | None``
|
@@ -261,11 +257,6 @@ class Formatter:
|
|
261
257
|
uncertainty mode (e.g. ``12.34(82)`` instead of
|
262
258
|
``12.34 ± 0.82``) should be used.
|
263
259
|
:type paren_uncertainty: ``bool | None``
|
264
|
-
:param pdg_sig_figs: Flag indicating whether the
|
265
|
-
particle-data-group conventions should be used to
|
266
|
-
automatically determine the number of significant figures to
|
267
|
-
use for uncertainty. Ignored for single value formatting.
|
268
|
-
:type pdg_sig_figs: ``bool | None``
|
269
260
|
:param left_pad_matching: Flag indicating if the value or
|
270
261
|
uncertainty should be left padded to ensure they are both left
|
271
262
|
padded to the same digits place.
|
@@ -312,7 +303,6 @@ class Formatter:
|
|
312
303
|
superscript=superscript,
|
313
304
|
nan_inf_exp=nan_inf_exp,
|
314
305
|
paren_uncertainty=paren_uncertainty,
|
315
|
-
pdg_sig_figs=pdg_sig_figs,
|
316
306
|
left_pad_matching=left_pad_matching,
|
317
307
|
paren_uncertainty_trim=paren_uncertainty_trim,
|
318
308
|
pm_whitespace=pm_whitespace,
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
-
from typing import TYPE_CHECKING
|
5
|
+
from typing import TYPE_CHECKING, Literal
|
6
6
|
|
7
7
|
from sciform.options import global_options, option_types
|
8
8
|
from sciform.options.conversion import populate_options
|
@@ -27,9 +27,9 @@ def get_global_options() -> PopulatedOptions:
|
|
27
27
|
def set_global_options( # noqa: PLR0913
|
28
28
|
*,
|
29
29
|
exp_mode: option_types.ExpMode | None = None,
|
30
|
-
exp_val: int |
|
30
|
+
exp_val: int | Literal["auto"] | None = None,
|
31
31
|
round_mode: option_types.RoundMode | None = None,
|
32
|
-
ndigits: int |
|
32
|
+
ndigits: int | Literal["all", "pdg"] | None = None,
|
33
33
|
upper_separator: option_types.UpperSeparators | None = None,
|
34
34
|
decimal_separator: option_types.DecimalSeparators | None = None,
|
35
35
|
lower_separator: option_types.LowerSeparators | None = None,
|
@@ -44,7 +44,6 @@ def set_global_options( # noqa: PLR0913
|
|
44
44
|
superscript: bool | None = None,
|
45
45
|
nan_inf_exp: bool | None = None,
|
46
46
|
paren_uncertainty: bool | None = None,
|
47
|
-
pdg_sig_figs: bool | None = None,
|
48
47
|
left_pad_matching: bool | None = None,
|
49
48
|
paren_uncertainty_trim: bool | None = None,
|
50
49
|
pm_whitespace: bool | None = None,
|
@@ -76,7 +75,6 @@ def set_global_options( # noqa: PLR0913
|
|
76
75
|
superscript=superscript,
|
77
76
|
nan_inf_exp=nan_inf_exp,
|
78
77
|
paren_uncertainty=paren_uncertainty,
|
79
|
-
pdg_sig_figs=pdg_sig_figs,
|
80
78
|
left_pad_matching=left_pad_matching,
|
81
79
|
paren_uncertainty_trim=paren_uncertainty_trim,
|
82
80
|
pm_whitespace=pm_whitespace,
|
@@ -110,9 +108,9 @@ class GlobalOptionsContext:
|
|
110
108
|
self: GlobalOptionsContext,
|
111
109
|
*,
|
112
110
|
exp_mode: option_types.ExpMode | None = None,
|
113
|
-
exp_val: int |
|
111
|
+
exp_val: int | option_types.ExpVal | None = None,
|
114
112
|
round_mode: option_types.RoundMode | None = None,
|
115
|
-
ndigits: int |
|
113
|
+
ndigits: int | option_types.NDigits | None = None,
|
116
114
|
upper_separator: option_types.UpperSeparators | None = None,
|
117
115
|
decimal_separator: option_types.DecimalSeparators | None = None,
|
118
116
|
lower_separator: option_types.LowerSeparators | None = None,
|
@@ -127,7 +125,6 @@ class GlobalOptionsContext:
|
|
127
125
|
superscript: bool | None = None,
|
128
126
|
nan_inf_exp: bool | None = None,
|
129
127
|
paren_uncertainty: bool | None = None,
|
130
|
-
pdg_sig_figs: bool | None = None,
|
131
128
|
left_pad_matching: bool | None = None,
|
132
129
|
paren_uncertainty_trim: bool | None = None,
|
133
130
|
pm_whitespace: bool | None = None,
|
@@ -154,7 +151,6 @@ class GlobalOptionsContext:
|
|
154
151
|
superscript=superscript,
|
155
152
|
nan_inf_exp=nan_inf_exp,
|
156
153
|
paren_uncertainty=paren_uncertainty,
|
157
|
-
pdg_sig_figs=pdg_sig_figs,
|
158
154
|
left_pad_matching=left_pad_matching,
|
159
155
|
paren_uncertainty_trim=paren_uncertainty_trim,
|
160
156
|
pm_whitespace=pm_whitespace,
|
@@ -15,13 +15,12 @@ from sciform.format_utils.numbers import (
|
|
15
15
|
from sciform.options.option_types import (
|
16
16
|
ExpFormatEnum,
|
17
17
|
ExpModeEnum,
|
18
|
+
ExpValEnum,
|
18
19
|
)
|
19
20
|
|
20
21
|
if TYPE_CHECKING: # pragma: no cover
|
21
22
|
from decimal import Decimal
|
22
23
|
|
23
|
-
from sciform.options.option_types import AutoExpVal
|
24
|
-
|
25
24
|
|
26
25
|
def get_translation_dict(
|
27
26
|
exp_format: ExpFormatEnum,
|
@@ -120,7 +119,7 @@ def get_val_unc_exp(
|
|
120
119
|
val: Decimal,
|
121
120
|
unc: Decimal,
|
122
121
|
exp_mode: ExpModeEnum,
|
123
|
-
input_exp: int |
|
122
|
+
input_exp: int | ExpValEnum,
|
124
123
|
) -> int:
|
125
124
|
"""Get exponent for value/uncertainty formatting."""
|
126
125
|
if val.is_finite() and unc.is_finite():
|
@@ -3,7 +3,7 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import decimal
|
6
|
-
from
|
6
|
+
from decimal import Decimal
|
7
7
|
|
8
8
|
from sciform.format_utils.numbers import (
|
9
9
|
get_top_dec_place,
|
@@ -14,9 +14,6 @@ from sciform.options.option_types import (
|
|
14
14
|
SignModeEnum,
|
15
15
|
)
|
16
16
|
|
17
|
-
if TYPE_CHECKING: # pragma: no cover
|
18
|
-
from decimal import Decimal
|
19
|
-
|
20
17
|
|
21
18
|
def get_sign_str(num: Decimal, sign_mode: SignModeEnum) -> str:
|
22
19
|
"""Get the format sign string."""
|
@@ -95,11 +92,26 @@ def construct_num_str(
|
|
95
92
|
return f"{sign_str}{pad_str}{abs_num_str}"
|
96
93
|
|
97
94
|
|
95
|
+
def parse_mantissa_str_to_dec(
|
96
|
+
mantissa_str: str,
|
97
|
+
decimal_separator: DecimalSeparatorEnums,
|
98
|
+
) -> Decimal:
|
99
|
+
"""Convert a string, possibly with non-standard separators, to a decimal."""
|
100
|
+
clean_mantissa_str = mantissa_str
|
101
|
+
for separator in SeparatorEnum:
|
102
|
+
if separator != decimal_separator:
|
103
|
+
clean_mantissa_str = clean_mantissa_str.replace(
|
104
|
+
separator,
|
105
|
+
"",
|
106
|
+
)
|
107
|
+
clean_mantissa_str = clean_mantissa_str.replace(decimal_separator, ".")
|
108
|
+
mantissa_dec = Decimal(clean_mantissa_str)
|
109
|
+
return mantissa_dec
|
110
|
+
|
111
|
+
|
98
112
|
def construct_val_unc_str( # noqa: PLR0913
|
99
113
|
val_mantissa_str: str,
|
100
114
|
unc_mantissa_str: str,
|
101
|
-
val_mantissa: Decimal,
|
102
|
-
unc_mantissa: Decimal,
|
103
115
|
decimal_separator: DecimalSeparatorEnums,
|
104
116
|
*,
|
105
117
|
paren_uncertainty: bool,
|
@@ -113,22 +125,20 @@ def construct_val_unc_str( # noqa: PLR0913
|
|
113
125
|
pm_symb = f" {pm_symb} "
|
114
126
|
val_unc_str = f"{val_mantissa_str}{pm_symb}{unc_mantissa_str}"
|
115
127
|
else:
|
116
|
-
if
|
117
|
-
|
118
|
-
|
119
|
-
and
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
unc_mantissa_str = unc_mantissa_str.replace(separator, "")
|
131
|
-
unc_mantissa_str = unc_mantissa_str.lstrip("0" + decimal_separator)
|
128
|
+
if paren_uncertainty_trim:
|
129
|
+
val_dec = parse_mantissa_str_to_dec(val_mantissa_str, decimal_separator)
|
130
|
+
unc_dec = parse_mantissa_str_to_dec(unc_mantissa_str, decimal_separator)
|
131
|
+
if unc_dec.is_finite() and val_dec.is_finite():
|
132
|
+
if unc_dec == 0:
|
133
|
+
unc_mantissa_str = "0"
|
134
|
+
elif unc_dec < abs(val_dec):
|
135
|
+
for separator in SeparatorEnum:
|
136
|
+
if separator != decimal_separator:
|
137
|
+
unc_mantissa_str = unc_mantissa_str.replace(
|
138
|
+
separator,
|
139
|
+
"",
|
140
|
+
)
|
141
|
+
unc_mantissa_str = unc_mantissa_str.lstrip("0" + decimal_separator)
|
132
142
|
val_unc_str = f"{val_mantissa_str}({unc_mantissa_str})"
|
133
143
|
return val_unc_str
|
134
144
|
|
sciform/format_utils/numbers.py
CHANGED
@@ -13,15 +13,14 @@ from sciform.formatting.parser import (
|
|
13
13
|
non_finite_val_pattern,
|
14
14
|
)
|
15
15
|
from sciform.options.option_types import (
|
16
|
-
AutoDigits,
|
17
|
-
AutoExpVal,
|
18
16
|
ExpModeEnum,
|
17
|
+
ExpValEnum,
|
19
18
|
)
|
20
19
|
|
21
20
|
|
22
21
|
def get_top_dec_place(num: Decimal) -> int:
|
23
22
|
"""Get the decimal place of a decimal's most significant digit."""
|
24
|
-
if not num.is_finite()
|
23
|
+
if not num.is_finite():
|
25
24
|
return 0
|
26
25
|
_, digits, exp = num.normalize().as_tuple()
|
27
26
|
return len(digits) + exp - 1
|
@@ -48,7 +47,7 @@ def get_val_unc_top_dec_place(
|
|
48
47
|
input_top_dec_place: int,
|
49
48
|
*,
|
50
49
|
left_pad_matching: bool,
|
51
|
-
) -> int
|
50
|
+
) -> int:
|
52
51
|
"""Get top decimal place for value/uncertainty formatting."""
|
53
52
|
if left_pad_matching:
|
54
53
|
val_top_dec_place = get_top_dec_place(val_mantissa)
|
@@ -65,10 +64,10 @@ def get_val_unc_top_dec_place(
|
|
65
64
|
|
66
65
|
|
67
66
|
def get_fixed_exp(
|
68
|
-
input_exp: int |
|
67
|
+
input_exp: int | ExpValEnum,
|
69
68
|
) -> Literal[0]:
|
70
69
|
"""Get the exponent for fixed or percent format modes."""
|
71
|
-
if input_exp is not
|
70
|
+
if input_exp is not ExpValEnum.AUTO and input_exp != 0:
|
72
71
|
msg = "Cannot set non-zero exponent in fixed point or percent exponent mode."
|
73
72
|
raise ValueError(msg)
|
74
73
|
return 0
|
@@ -76,20 +75,20 @@ def get_fixed_exp(
|
|
76
75
|
|
77
76
|
def get_scientific_exp(
|
78
77
|
num: Decimal,
|
79
|
-
input_exp: int |
|
78
|
+
input_exp: int | ExpValEnum,
|
80
79
|
) -> int:
|
81
80
|
"""Get the exponent for scientific formatting mode."""
|
82
|
-
return get_top_dec_place(num) if input_exp is
|
81
|
+
return get_top_dec_place(num) if input_exp is ExpValEnum.AUTO else input_exp
|
83
82
|
|
84
83
|
|
85
84
|
def get_engineering_exp(
|
86
85
|
num: Decimal,
|
87
|
-
input_exp: int |
|
86
|
+
input_exp: int | ExpValEnum,
|
88
87
|
*,
|
89
88
|
shifted: bool = False,
|
90
89
|
) -> int:
|
91
90
|
"""Get the exponent for engineering formatting modes."""
|
92
|
-
if input_exp is
|
91
|
+
if input_exp is ExpValEnum.AUTO:
|
93
92
|
exp_val = get_top_dec_place(num)
|
94
93
|
exp_val = exp_val // 3 * 3 if not shifted else (exp_val + 1) // 3 * 3
|
95
94
|
else:
|
@@ -105,12 +104,12 @@ def get_engineering_exp(
|
|
105
104
|
|
106
105
|
def get_binary_exp(
|
107
106
|
num: Decimal,
|
108
|
-
input_exp: int |
|
107
|
+
input_exp: int | ExpValEnum,
|
109
108
|
*,
|
110
109
|
iec: bool = False,
|
111
110
|
) -> int:
|
112
111
|
"""Get the exponent for binary formatting modes."""
|
113
|
-
if input_exp is
|
112
|
+
if input_exp is ExpValEnum.AUTO:
|
114
113
|
exp_val = get_top_dec_place_binary(num)
|
115
114
|
if iec:
|
116
115
|
exp_val = (exp_val // 10) * 10
|
@@ -128,7 +127,7 @@ def get_binary_exp(
|
|
128
127
|
def get_mantissa_exp_base(
|
129
128
|
num: Decimal,
|
130
129
|
exp_mode: ExpModeEnum,
|
131
|
-
input_exp: int |
|
130
|
+
input_exp: int | ExpValEnum,
|
132
131
|
) -> tuple[Decimal, int, int]:
|
133
132
|
"""Get mantissa, exponent, and base for formatting a decimal number."""
|
134
133
|
if exp_mode is ExpModeEnum.BINARY or exp_mode is ExpModeEnum.BINARY_IEC:
|
@@ -138,7 +137,7 @@ def get_mantissa_exp_base(
|
|
138
137
|
|
139
138
|
if num == 0 or not num.is_finite():
|
140
139
|
mantissa = Decimal(num)
|
141
|
-
exp = 0 if input_exp is
|
140
|
+
exp = 0 if input_exp is ExpValEnum.AUTO else input_exp
|
142
141
|
else:
|
143
142
|
if exp_mode is ExpModeEnum.FIXEDPOINT or exp_mode is ExpModeEnum.PERCENT:
|
144
143
|
exp = get_fixed_exp(input_exp)
|
sciform/format_utils/rounding.py
CHANGED
@@ -8,10 +8,7 @@ from sciform.format_utils.numbers import (
|
|
8
8
|
get_bottom_dec_place,
|
9
9
|
get_top_dec_place,
|
10
10
|
)
|
11
|
-
from sciform.options.option_types import
|
12
|
-
AutoDigits,
|
13
|
-
RoundModeEnum,
|
14
|
-
)
|
11
|
+
from sciform.options.option_types import RoundModeEnum
|
15
12
|
|
16
13
|
|
17
14
|
def get_pdg_round_digit(num: Decimal) -> int:
|
@@ -28,11 +25,13 @@ def get_pdg_round_digit(num: Decimal) -> int:
|
|
28
25
|
if not num.is_finite():
|
29
26
|
msg = f"num must be finite, not {num}."
|
30
27
|
raise ValueError(msg)
|
28
|
+
if num == 0:
|
29
|
+
return 0
|
31
30
|
|
32
31
|
top_dec_place = get_top_dec_place(num)
|
33
32
|
|
34
33
|
# Bring num to be between 100 and 1000.
|
35
|
-
num_top_three_digs = num * Decimal(10) ** (Decimal(2) - Decimal(top_dec_place))
|
34
|
+
num_top_three_digs = abs(num) * Decimal(10) ** (Decimal(2) - Decimal(top_dec_place))
|
36
35
|
num_top_three_digs = num_top_three_digs.quantize(1, rounding="ROUND_FLOOR")
|
37
36
|
new_top_dec_place = get_top_dec_place(num_top_three_digs)
|
38
37
|
num_top_three_digs = num_top_three_digs * 10 ** (2 - new_top_dec_place)
|
@@ -59,21 +58,21 @@ def get_pdg_round_digit(num: Decimal) -> int:
|
|
59
58
|
def get_round_dec_place(
|
60
59
|
num: Decimal,
|
61
60
|
round_mode: RoundModeEnum,
|
62
|
-
ndigits: int
|
63
|
-
*,
|
64
|
-
pdg_sig_figs: bool = False,
|
61
|
+
ndigits: int,
|
65
62
|
) -> int:
|
66
63
|
"""Get the decimal place to which to round."""
|
67
64
|
# TODO: Handle nan and inf
|
68
|
-
if round_mode is RoundModeEnum.
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
65
|
+
if round_mode is RoundModeEnum.ALL:
|
66
|
+
round_digit = get_bottom_dec_place(num)
|
67
|
+
elif round_mode is RoundModeEnum.PDG:
|
68
|
+
round_digit = get_pdg_round_digit(num)
|
69
|
+
elif round_mode is RoundModeEnum.SIG_FIG:
|
70
|
+
if num == 0:
|
71
|
+
round_digit = 0
|
73
72
|
else:
|
74
73
|
round_digit = get_top_dec_place(num) - (ndigits - 1)
|
75
74
|
elif round_mode is RoundModeEnum.DEC_PLACE:
|
76
|
-
round_digit =
|
75
|
+
round_digit = -ndigits
|
77
76
|
else:
|
78
77
|
msg = f"Unhandled round mode: {round_mode}."
|
79
78
|
raise ValueError(msg)
|
@@ -83,17 +82,15 @@ def get_round_dec_place(
|
|
83
82
|
def round_val_unc(
|
84
83
|
val: Decimal,
|
85
84
|
unc: Decimal,
|
86
|
-
|
87
|
-
|
88
|
-
use_pdg_sig_figs: bool = False,
|
85
|
+
round_mode: RoundModeEnum,
|
86
|
+
ndigits: int,
|
89
87
|
) -> tuple[Decimal, Decimal, int]:
|
90
88
|
"""Simultaneously round the value and uncertainty."""
|
91
89
|
if unc.is_finite() and unc != 0:
|
92
90
|
round_digit = get_round_dec_place(
|
93
91
|
unc,
|
94
|
-
|
92
|
+
round_mode,
|
95
93
|
ndigits,
|
96
|
-
pdg_sig_figs=use_pdg_sig_figs,
|
97
94
|
)
|
98
95
|
unc_rounded = round(unc, -round_digit)
|
99
96
|
if val.is_finite():
|
@@ -103,9 +100,8 @@ def round_val_unc(
|
|
103
100
|
elif val.is_finite():
|
104
101
|
round_digit = get_round_dec_place(
|
105
102
|
val,
|
106
|
-
|
103
|
+
round_mode,
|
107
104
|
ndigits,
|
108
|
-
pdg_sig_figs=False,
|
109
105
|
)
|
110
106
|
unc_rounded = unc
|
111
107
|
val_rounded = round(val, -round_digit)
|
sciform/formatting/fsml.py
CHANGED
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
5
5
|
import re
|
6
6
|
|
7
7
|
from sciform.options.input_options import InputOptions
|
8
|
+
from sciform.options.option_types import RoundModeEnum
|
8
9
|
|
9
10
|
pattern = re.compile(
|
10
11
|
r"""^
|
@@ -12,7 +13,11 @@ pattern = re.compile(
|
|
12
13
|
(?P<sign_mode>[-+ ])?
|
13
14
|
(?P<alternate_mode>\#)?
|
14
15
|
(?P<left_pad_dec_place>\d+)?
|
15
|
-
(?:
|
16
|
+
(?:
|
17
|
+
(?P<round_mode>[.!])(?P<ndigits>([+-]?\d+))
|
18
|
+
|
|
19
|
+
(?P<round_mode_special>[AP])
|
20
|
+
)?
|
16
21
|
(?P<exp_mode>[fF%eErRbB])?
|
17
22
|
(?:x(?P<exp_val>[+-]?\d+))?
|
18
23
|
(?P<prefix_mode>p)?
|
@@ -51,6 +56,15 @@ def parse_exp_mode(
|
|
51
56
|
return exp_mode, capitalize
|
52
57
|
|
53
58
|
|
59
|
+
ROUND_MODE_MAPPING = {
|
60
|
+
"!": "sig_fig",
|
61
|
+
".": "dec_place",
|
62
|
+
"A": RoundModeEnum.ALL,
|
63
|
+
"P": RoundModeEnum.PDG,
|
64
|
+
None: None,
|
65
|
+
}
|
66
|
+
|
67
|
+
|
54
68
|
def format_options_from_fmt_spec(fmt_spec: str) -> InputOptions:
|
55
69
|
"""Resolve InputOptions from format specification string."""
|
56
70
|
match = pattern.match(fmt_spec)
|
@@ -68,16 +82,17 @@ def format_options_from_fmt_spec(fmt_spec: str) -> InputOptions:
|
|
68
82
|
if left_pad_dec_place is not None:
|
69
83
|
left_pad_dec_place = int(left_pad_dec_place)
|
70
84
|
|
71
|
-
round_mode_mapping = {"!": "sig_fig", ".": "dec_place", None: None}
|
72
|
-
|
73
85
|
round_mode_flag = match.group("round_mode")
|
74
|
-
round_mode = round_mode_mapping[round_mode_flag]
|
75
|
-
|
76
86
|
ndigits = match.group("ndigits")
|
77
|
-
if
|
78
|
-
|
87
|
+
if not round_mode_flag:
|
88
|
+
special_round_mode_flag = match.group("round_mode_special")
|
89
|
+
if not special_round_mode_flag:
|
90
|
+
round_mode = None
|
91
|
+
else:
|
92
|
+
round_mode = ROUND_MODE_MAPPING[special_round_mode_flag]
|
79
93
|
else:
|
80
|
-
|
94
|
+
round_mode = ROUND_MODE_MAPPING[round_mode_flag]
|
95
|
+
ndigits = int(ndigits)
|
81
96
|
|
82
97
|
exp_mode = match.group("exp_mode")
|
83
98
|
exp_mode, capitalize = parse_exp_mode(
|