Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Сomparing large numbers doesn't work #181

Open
partizanes opened this issue Dec 11, 2023 · 1 comment
Open

Сomparing large numbers doesn't work #181

partizanes opened this issue Dec 11, 2023 · 1 comment
Labels

Comments

@partizanes
Copy link

python3.8, python3.9

Test code:

from dictdiffer import diff

dict1 = dict({'test': {'serial_number': 22570409781991170591038650551}})
dict2 = dict({'test': {'serial_number': 22570409781991170591038650552}})
print(list(diff(dict1, dict2)))

Return empty list.

The problem was using math.isclose which produces false results:

def are_different(first, second, tolerance, absolute_tolerance=None):
    """Check if 2 values are different.

    In case of numerical values, the tolerance is used to check if the values
    are different.
    In all other cases, the difference is straight forward.
    """
    if first == second:
        # values are same - simple case
        return False

    first_is_nan, second_is_nan = bool(first != first), bool(second != second)

    if first_is_nan or second_is_nan:
        # two 'NaN' values are not different (see issue #114)
        return not (first_is_nan and second_is_nan)
    elif isinstance(first, num_types) and isinstance(second, num_types):
        # two numerical values are compared with tolerance
        return not math.isclose(
            first,
            second,
            rel_tol=tolerance or 0,
            abs_tol=absolute_tolerance or 0,
        )
    # we got different values
    return True

Test example:

import math  

print(math.isclose(22570409781991170591038650551, 22570409781991170591038650552, rel_tol=0.0, abs_tol=0.0)) # TRUE
print(22570409781991170591038650551 == 22570409781991170591038650552)                                       # FALSE

@dbrakman
Copy link

The standard library says here that rel_tol must be greater than 0.

In your specific example, dictdiffer can produce the desired output with the following:

from decimal import Decimal
from dictdiffer import diff

dict1 = dict({'test': {'serial_number': 22570409781991170591038650551}})
dict2 = dict({'test': {'serial_number': 22570409781991170591038650552}})
print(list(diff(dict1, dict2, tolerance=Decimal(f"1e-{sys.get_int_max_str_digits()}"))))

However, since python3 has no maximum integer, I think a general solution for allowing requiring exact equality between integers would require a change to the library. I was looking at the same code in wondering how to dictdiffer should compare Decimal(1.99) to Decimal("1.99"), and after looking further into the subclasses of number.Number came to the conclusion that tolerating floating-point imprecision is appropriate only when both operands are floats.

This is a judgement call for the maintainers that is arguably a breaking change, but I think it's consistent with the docstring and all the existing tests and that the alternative is a can of worms with too many edge cases. Proposed #183

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants