Skip to content

Commit 34961ec

Browse files
committed
Fix quantity rounding formatting bug
Certain numbers would be rendered wrongly due to rounding in the python formatter, e.g. Voltage.from_volts(999.9999850988388) would render "1000 V". This results in a faulty round-trip conversion (`num -> str -> num` no longer matching). Signed-off-by: Mathias L. Baumann <[email protected]>
1 parent 493df59 commit 34961ec

File tree

2 files changed

+19
-2
lines changed

2 files changed

+19
-2
lines changed

src/frequenz/sdk/timeseries/_quantities.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ def __str__(self) -> str:
199199
"""
200200
return self.__format__("")
201201

202+
# pylint: disable=too-many-branches
202203
def __format__(self, __format_spec: str) -> str:
203204
"""Return a formatted string representation of this quantity.
204205
@@ -250,8 +251,22 @@ def __format__(self, __format_spec: str) -> str:
250251
if math.isinf(self._base_value) or math.isnan(self._base_value):
251252
return f"{self._base_value} {self._exponent_unit_map[0]}"
252253

253-
abs_value = abs(self._base_value)
254-
exponent = math.floor(math.log10(abs_value)) if abs_value else 0
254+
if abs_value := abs(self._base_value):
255+
precision_pow = 10 ** (precision)
256+
# Prevent numbers like 999.999999 being rendered as 1000 V
257+
# instead of 1 kV.
258+
# This could happen because the str formatting function does
259+
# rounding as well.
260+
# This is an imperfect solution that works for _most_ cases.
261+
# isclose parameters were chosen according to the observed cases
262+
if math.isclose(abs_value, precision_pow, abs_tol=1e-4, rel_tol=0.01):
263+
# If the value is close to the precision, round it
264+
exponent = math.ceil(math.log10(precision_pow))
265+
else:
266+
exponent = math.floor(math.log10(abs_value))
267+
else:
268+
exponent = 0
269+
255270
unit_place = exponent - exponent % 3
256271
if unit_place < min(self._exponent_unit_map):
257272
unit = self._exponent_unit_map[min(self._exponent_unit_map.keys())]

tests/timeseries/test_quantities.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ def test_string_representation() -> None:
107107
assert (
108108
repr(Quantity(1.024445, exponent=0)) == "Quantity(value=1.024445, exponent=0)"
109109
)
110+
assert f"{Quantity(0.50001, exponent=0):.0}" == "1"
110111
assert f"{Quantity(1.024445, exponent=0)}" == "1.024"
111112
assert f"{Quantity(1.024445, exponent=0):.0}" == "1"
112113
assert f"{Quantity(0.124445, exponent=0):.0}" == "0"
@@ -154,6 +155,7 @@ def test_string_representation() -> None:
154155
assert f"{Energy.from_watt_hours(0.124445):.0}" == "0 Wh"
155156
assert f"{Power.from_watts(-0.0):.0}" == "-0 W"
156157
assert f"{Power.from_watts(0.0):.0}" == "0 W"
158+
assert f"{Voltage.from_volts(999.9999850988388)}" == "1 kV"
157159

158160

159161
def test_isclose() -> None:

0 commit comments

Comments
 (0)