2

Why does Pylint think this is a redundant comparison? Is this not the fastest way to check for NaN?

Refactor: R0124
Redundant comparison - value_1 != value_1
Redundant comparison - value_2 != value_2

How else am I supposed to check if two values are equal including when they're nan?

NaN = float("NaN")

def compare(value_1, value_2):
    match_nan = value_1 != value_1
    print(match_nan and
          value_2 != value_2 or
          value_1 == value_2)

compare(1, 1)
compare(1, NaN)
compare(NaN, 1)
compare(NaN, NaN)

Output:

True
False
False
True

Now, sure math.is_nan is a better solution if you're working with custom classes:

from math import isnan

class NotEQ:
    def __eq__(self, other):
        return False
not_eq = NotEQ()
print(not_eq != not_eq)
print(isnan(not_eq))

Output:

True
... TypeError: must be real number, not NotEQ

I'm writing a JSON patcher, and I don't think the normal behaviour is very useful when you want to be able to remove them from lists, or raise an error is two values aren't equal (but allowing NaN and NaN)

Nineteendo
  • 882
  • 3
  • 18
  • 1
    Is this what you're looking for? [How can I check for NaN values?](/q/944700/4518341) – wjandrea Dec 22 '22 at 22:23
  • I think this comment mentioned that checking for inequality is the fasted: https://stackoverflow.com/a/62171968/13454049 – Nineteendo Dec 22 '22 at 22:26
  • But my issue is that pylint doesn't think that's a good idea. – Nineteendo Dec 22 '22 at 22:27
  • That answer is using a bad test; I put a comment there to explain. Either way though, a difference of 50 nanoseconds is really small, and I doubt that'd actually be a bottleneck. – wjandrea Dec 23 '22 at 00:17

2 Answers2

1

Use math.isnan(x); it's just as fast as x != x. The answer you cited that says x != x is fastest is using a bad test (and I put a comment there to explain). Here's a fixed version:

In [1]: %%timeit x = float('nan')
   ...: x != x
   ...: 
   ...: 
36 ns ± 0.51 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)

In [2]: %%timeit x = float('nan'); from math import isnan
   ...: isnan(x)
   ...: 
   ...: 
35.8 ns ± 0.282 ns per loop (mean ± std. dev. of 7 runs, 10,000,000 loops each)
wjandrea
  • 28,235
  • 9
  • 60
  • 81
  • But there's one more problem: I need to check first if the number is a float before calling this function – Nineteendo Dec 23 '22 at 07:12
  • Here is that question: https://stackoverflow.com/q/74897182/13454049 – Nineteendo Dec 23 '22 at 07:32
  • @Nineteendo It'll take any number that can be *converted* to a float, for example, `math.isnan(1)` -> `False`. Not quite the same thing. – wjandrea Dec 23 '22 at 17:31
  • Yeah, you're right, that makes it even less convenient. I have to check if it's a float or int... – Nineteendo Dec 24 '22 at 17:18
  • Actually no, because when it's an int, it can't be NaN. – Nineteendo Dec 24 '22 at 17:26
  • @Nineteendo It'd probably make more sense to check if it's a [`Real`](https://docs.python.org/3/library/numbers.html#numbers.Real). That'll handle even NaN ints (hypothetically). – wjandrea Dec 24 '22 at 20:25
  • Eh, I guess that matches both, but I don't think it's a good practice to use NaN ints... Integers are meant for values in a small range. – Nineteendo Dec 24 '22 at 21:33
-1

pylint throws this issue whenever you compare either a constant or a variable with itself. I case of a variable it compares the name (see _check_logical_tautology in the source). So you could circumvent this by renaming one side of the comparison like this:

def compare(value_1, value_2):
    v_1 = value_1
    v_2 = value_2
    match_nan = v_1 != value_1
    print(match_nan and v_2 != value_2 or value_1 == value_2)

at least to get rid of the linting issue. However I would also follow wjandrea's advice and go for How can I check for NaN values?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
sandro
  • 1
  • 2
  • Don't change the code, just [add a linter ignore](https://docs.pylint.org/faq.html#is-it-possible-to-locally-disable-a-particular-message). – wjandrea Dec 24 '22 at 20:28
  • I've done that, there's no good reason to use isnan for me, it will be slower and harder to work with. – Nineteendo Dec 24 '22 at 21:25