From 3e49e5520fd9a36e5ba86818fb4de9707c4759a6 Mon Sep 17 00:00:00 2001 From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Sun, 2 Aug 2026 03:12:08 -0700 Subject: [PATCH] fix: handle negative imaginary parts in number_to_string number_to_string built the normalized complex string by unconditionally joining the real and imaginary parts with '+'. When the imaginary part is negative the rendered part already carries its own sign, producing a malformed literal such as '1.0+-1.0j', which complex() rejects with ValueError. Any comparison involving such a number crashed. The sign separator is now omitted when the rendered imaginary part is already negative. Regression cases added to the existing test_number_to_string_complex_digits parametrize list. Closes #551 --- CHANGELOG.md | 3 +++ deepdiff/helper.py | 26 ++++++++++++++------------ tests/test_helper.py | 4 ++++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46217041..575479e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # DeepDiff Change log +- Unreleased + - Fixed `number_to_string` raising `ValueError` for complex numbers with a negative imaginary part + - v9-1-0 - Added multiprocessing support for DeepDiff: parallel distance computation and parallel subtree diffing with aggregated worker stats, deterministic ordering, and automatic fallback to serial when unsafe (e.g. `custom_operators`, `*_obj_callback`, `ignore_order_func`) - Added wildcard/glob pattern support for `exclude_paths` and `include_paths` thanks to [akshat62](https://github.com/akshat62) diff --git a/deepdiff/helper.py b/deepdiff/helper.py index 3fc61183..8b6d1acf 100644 --- a/deepdiff/helper.py +++ b/deepdiff/helper.py @@ -496,19 +496,21 @@ def number_to_string(number: Any, significant_digits: int, number_format_notatio number = int(number) elif isinstance(number, only_complex_number): # type: ignore # Case for complex numbers. + real = number_to_string( + number=number.real, # type: ignore + significant_digits=significant_digits, + number_format_notation=number_format_notation + ) + imag = number_to_string( + number=number.imag, # type: ignore + significant_digits=significant_digits, + number_format_notation=number_format_notation + ) + # A negative imaginary part already carries its own sign. Adding another + # one produces a malformed string such as "1.0+-1.0j" that complex() rejects. + sign = '' if str(imag).startswith('-') else '+' number = number.__class__( - "{real}+{imag}j".format( # type: ignore - real=number_to_string( - number=number.real, # type: ignore - significant_digits=significant_digits, - number_format_notation=number_format_notation - ), - imag=number_to_string( - number=number.imag, # type: ignore - significant_digits=significant_digits, - number_format_notation=number_format_notation - ) - ) # type: ignore + "{real}{sign}{imag}j".format(real=real, sign=sign, imag=imag) # type: ignore ) else: number = round(number=number, ndigits=significant_digits) # type: ignore diff --git a/tests/test_helper.py b/tests/test_helper.py index 30942efe..2e1bff1b 100644 --- a/tests/test_helper.py +++ b/tests/test_helper.py @@ -184,6 +184,10 @@ def test_number_to_string_decimal_digits(self, t1, t2, significant_digits, numbe (-0j, 0.2j, 5, "e", ('0.00000e+0', '0.00000e+0+2.00000e-1j')), (-0j, 0.2j, 0, "f", True), (-0j, 0.2j, 0, "e", True), + (1-1j, 1-1j, 1, "f", True), + (1-1j, 1.2-1.2j, 1, "f", ('1.0-1.0j', '1.2-1.2j')), + (1-1j, 1.2-1.2j, 1, "e", ('1.0e+0-1.0e+0j', '1.2e+0-1.2e+0j')), + (-1-1j, -1.2-1.2j, 1, "f", ('-1.0-1.0j', '-1.2-1.2j')), ]) def test_number_to_string_complex_digits(self, t1, t2, significant_digits, number_format_notation, expected_result): st1 = number_to_string(t1, significant_digits=significant_digits, number_format_notation=number_format_notation)