From 65ff3d36d24b11d71ed4821235df31eee3caa1e2 Mon Sep 17 00:00:00 2001 From: Paul Saxe Date: Sat, 22 Nov 2025 13:46:55 -0500 Subject: [PATCH 1/3] Bugfix: fixed numerical precision in For loops * Fixed a problem caused by numerical imprecision of For loops with floating points values causing termination too early, and also ugly directory names, etc. like 3.00000000002 or 2.999999999998 --- HISTORY.rst | 5 +++++ loop_step/loop.py | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index be57a5b..10cdf50 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,6 +1,11 @@ ======= History ======= +2025.9.22: Bugfix: fixed numerical precision in For loops + * Fixed a problem caused by numerical imprecision of For loops with floating points + values causing termination too early, and also ugly directory names, etc. like + 3.00000000002 or 2.999999999998 + 2025.7.23: Bugfix: options not properly set if the loop is in a subflowchart * The options of the steps in the loop were not set correctly if the loop was in a subflowchart in e.g. a substep step or training step. diff --git a/loop_step/loop.py b/loop_step/loop.py index 018be31..25265b1 100644 --- a/loop_step/loop.py +++ b/loop_step/loop.py @@ -256,11 +256,27 @@ def run(self): self.set_variable(P["variable"], self._loop_value) # Loop to get length... range doesn't work for nonintegers + # If floating point, remove nasty extra digits + ndigits = 0 + if isinstance(start, float): + tmp = str(start).split(".") + if len(tmp) == 2: + tmp = len(tmp[1]) + if tmp > ndigits: + ndigits = tmp + if isinstance(step, float): + tmp = str(step).split(".") + if len(tmp) == 2: + tmp = len(tmp[1]) + if tmp > ndigits: + ndigits = tmp count = 0 tmp = start while tmp <= end: count += 1 tmp += step + if ndigits > 0: + tmp = round(tmp, ndigits) self._loop_length = count printer.important( __( @@ -520,6 +536,8 @@ def run(self): self._loop_count += 1 if self._loop_count > 1: self._loop_value += step + if ndigits > 0: + self._loop_value = round(self._loop_value, ndigits) self.set_variable(P["variable"], self._loop_value) From 3eca3cdb3f8e7fde72ed8f625e39c5fbb1e38c40 Mon Sep 17 00:00:00 2001 From: Paul Saxe Date: Sat, 22 Nov 2025 14:11:54 -0500 Subject: [PATCH 2/3] More carefully checking start, end and step in For loops for decimal precision and also ensuring the iteration directory names are properly formatted. --- loop_step/loop.py | 48 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/loop_step/loop.py b/loop_step/loop.py index 25265b1..29164bd 100644 --- a/loop_step/loop.py +++ b/loop_step/loop.py @@ -25,6 +25,16 @@ printer = printing.getPrinter("loop") +def _get_decimal_places(value): + """Get the number of decimal places in a float.""" + if not isinstance(value, float): + return 0 + parts = str(value).split(".") + if len(parts) == 2: + return len(parts[1]) + return 0 + + class BreakLoop(Exception): """Indicates that SEAMM should break from the loop""" @@ -217,30 +227,33 @@ def run(self): # Some local variables need each iteration # See if loop variables are all integers - integers = True start = P["start"] if isinstance(start, str): start = float(start) if start.is_integer(): start = int(start) - else: - integers = False + step = P["step"] if isinstance(step, str): step = float(step) if step.is_integer(): step = int(step) - else: - integers = False + end = P["end"] if isinstance(end, str): end = float(end) if end.is_integer(): end = int(end) - else: - integers = False - if integers: + # If floating point, remove nasty extra digits + ndigits = max( + _get_decimal_places(start), + _get_decimal_places(step), + _get_decimal_places(end), + ) + if ndigits > 0: + fmt = f".{ndigits}f" + else: fmt = f"0{len(str(end))}d" if self._loop_value is None: @@ -256,20 +269,6 @@ def run(self): self.set_variable(P["variable"], self._loop_value) # Loop to get length... range doesn't work for nonintegers - # If floating point, remove nasty extra digits - ndigits = 0 - if isinstance(start, float): - tmp = str(start).split(".") - if len(tmp) == 2: - tmp = len(tmp[1]) - if tmp > ndigits: - ndigits = tmp - if isinstance(step, float): - tmp = str(step).split(".") - if len(tmp) == 2: - tmp = len(tmp[1]) - if tmp > ndigits: - ndigits = tmp count = 0 tmp = start while tmp <= end: @@ -541,9 +540,8 @@ def run(self): self.set_variable(P["variable"], self._loop_value) - # For integer loops, we can use the value for the directory names - if integers: - self._custom_directory_name = f"iter_{self._loop_value:{fmt}}" + # Use the value for the directory names + self._custom_directory_name = f"iter_{self._loop_value:{fmt}}" # Set up the index variables tmp = self.get_variable("_loop_indices") From 1df53dc63cde97d60972b36228f3d744cbce7ca6 Mon Sep 17 00:00:00 2001 From: Paul Saxe Date: Sat, 22 Nov 2025 14:14:16 -0500 Subject: [PATCH 3/3] Bugfix: fixed floating-point precision issues in For loops * This fix addresses floating-point precision issues in For loops that caused early termination and generated malformed directory names (e.g., "3.00000000002" or "2.999999999998"). The fix calculates the number of decimal places from loop parameters and applies rounding to maintain precision throughout loop iterations. --- HISTORY.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 10cdf50..fe16706 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,10 +1,11 @@ ======= History ======= -2025.9.22: Bugfix: fixed numerical precision in For loops - * Fixed a problem caused by numerical imprecision of For loops with floating points - values causing termination too early, and also ugly directory names, etc. like - 3.00000000002 or 2.999999999998 +2025.9.22: Bugfix: fixed floating-point precision issues in For loops + * This fix addresses floating-point precision issues in For loops that caused early + termination and generated malformed directory names (e.g., "3.00000000002" or + "2.999999999998"). The fix calculates the number of decimal places from loop + parameters and applies rounding to maintain precision throughout loop iterations. 2025.7.23: Bugfix: options not properly set if the loop is in a subflowchart * The options of the steps in the loop were not set correctly if the loop was in a