From 6580a6bc012ac43bba8d0ba9377a8d33184433ac Mon Sep 17 00:00:00 2001 From: Dante Date: Thu, 26 Mar 2026 07:06:34 +0900 Subject: [PATCH] fix(number-convert): preserve int precision for large numbers (#13147) --- comfy_extras/nodes_number_convert.py | 17 +++++- .../nodes_number_convert_test.py | 57 +++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/comfy_extras/nodes_number_convert.py b/comfy_extras/nodes_number_convert.py index b2822c856..cac7e736d 100644 --- a/comfy_extras/nodes_number_convert.py +++ b/comfy_extras/nodes_number_convert.py @@ -44,8 +44,13 @@ class NumberConvertNode(io.ComfyNode): def execute(cls, value) -> io.NodeOutput: if isinstance(value, bool): float_val = 1.0 if value else 0.0 - elif isinstance(value, (int, float)): + int_val = 1 if value else 0 + elif isinstance(value, int): float_val = float(value) + int_val = value + elif isinstance(value, float): + float_val = value + int_val = int(value) elif isinstance(value, str): text = value.strip() if not text: @@ -56,6 +61,14 @@ class NumberConvertNode(io.ComfyNode): raise ValueError( f"Cannot convert string to number: {value!r}" ) from None + if not math.isfinite(float_val): + raise ValueError( + f"Cannot convert non-finite value to number: {float_val}" + ) + try: + int_val = int(text) + except ValueError: + int_val = int(float_val) else: raise TypeError( f"Unsupported input type: {type(value).__name__}" @@ -66,7 +79,7 @@ class NumberConvertNode(io.ComfyNode): f"Cannot convert non-finite value to number: {float_val}" ) - return io.NodeOutput(float_val, int(float_val)) + return io.NodeOutput(float_val, int_val) class NumberConvertExtension(ComfyExtension): diff --git a/tests-unit/comfy_extras_test/nodes_number_convert_test.py b/tests-unit/comfy_extras_test/nodes_number_convert_test.py index 0046fa8f4..fa3251abc 100644 --- a/tests-unit/comfy_extras_test/nodes_number_convert_test.py +++ b/tests-unit/comfy_extras_test/nodes_number_convert_test.py @@ -90,6 +90,63 @@ class TestNumberConvertExecute: assert result[0] == 1000.0 assert result[1] == 1000 + # --- Large number precision (string input) --- + + def test_string_large_int_above_2_53(self): + """Text-to-int must not lose precision for integers beyond 2^53.""" + big = 2**53 + 1 # 9007199254740993 + result = self._exec(str(big)) + assert result[1] == big + + def test_string_large_negative_int_above_2_53(self): + big = -(2**53 + 1) + result = self._exec(str(big)) + assert result[1] == big + + def test_string_very_large_int(self): + big = 2**63 + 42 + result = self._exec(str(big)) + assert result[1] == big + + def test_string_large_int_float_output_is_float(self): + """FLOAT output is still a float (may lose precision, but must be float type).""" + result = self._exec(str(2**53 + 1)) + assert isinstance(result[0], float) + + # --- Large number precision (int input) --- + + def test_int_large_above_2_53(self): + """Native int input must preserve its value in the INT output.""" + big = 2**53 + 1 + result = self._exec(big) + assert result[1] == big + + def test_int_large_negative_above_2_53(self): + big = -(2**53 + 1) + result = self._exec(big) + assert result[1] == big + + def test_int_very_large(self): + big = 2**100 + result = self._exec(big) + assert result[1] == big + + # --- String decimal / scientific notation fallback --- + + def test_string_decimal_still_truncates(self): + """Strings with decimal points fall back to int(float(...)) truncation.""" + result = self._exec("3.7") + assert result[1] == 3 + + def test_string_negative_decimal_truncates(self): + result = self._exec("-2.9") + assert result[1] == -2 + + def test_string_scientific_large(self): + result = self._exec("1e18") + assert result[0] == 1e18 + assert result[1] == 10**18 + # --- STRING error paths --- def test_empty_string_raises(self):