9

Why does Python return True when I compare int and float objects which have the same value?

For example:

>>> 5*2 == 5.0*2.0
True
user2357112
  • 260,549
  • 28
  • 431
  • 505
gaurav
  • 415
  • 5
  • 12
  • 4
    To answer the title: *virtually never*. This has little to do with type conversion, but with comparison logic. – deceze Sep 28 '18 at 14:10
  • 2
    You may find [this answer on CPython comparison between `int` and `float`](https://stackoverflow.com/a/30100743/9209546) helpful. While not guaranteed, I wouldn't be surprised if other implementations are similar. – jpp Sep 28 '18 at 14:30

7 Answers7

8

It's not as simple as a type conversion.

10 == 10.0 delegates to the arguments' __eq__ methods, trying (10).__eq__(10.0) first, and then (10.0).__eq__(10) if the first call returns NotImplemented. It makes no attempt to convert types. (Technically, the method lookup uses a special routine that bypasses instance __dict__ entries and __getattribute__/__getattr__ overrides, so it's not quite equivalent to calling the methods yourself.)

int.__eq__ has no idea how to handle a float:

>>> (10).__eq__(10.0)
NotImplemented

but float.__eq__ knows how to handle ints:

>>> (10.0).__eq__(10)
True

float.__eq__ isn't just performing a cast internally, either. It has over 100 lines of code to handle float/int comparison without the rounding error an unchecked cast could introduce. (Some of that could be simplified if the C-level comparison routine didn't also have to handle >, >=, <, and <=.)

user2357112
  • 260,549
  • 28
  • 431
  • 505
chepner
  • 497,756
  • 71
  • 530
  • 681
  • The next question is, I guess, does "the implementation of `int.__eq__`" require type conversion? Agree at Python level there's no conversion, but I'm not sure there's no conversion *anywhere*. – jpp Sep 28 '18 at 14:12
  • That is, by definition, an implementation detail. CPython, PyPy, and Jython, for instance, may all do different things. But the *language* does not specify that `int.__eq__(10, 10.0)` be turned into `float.__eq__(10.0, 10.0)`. – chepner Sep 28 '18 at 14:15
  • Since this SO and we don't want to have half-truths on SO, I have to well-actually you: `a == b` is **not** equivalent to `a.__eq__(b)`. The comparison mechanism is more elaborate than that. For example, if `a` doesn't support comparison with `b`, `b.__eq__(a)` is used instead. More details [here](https://docs.python.org/3/reference/datamodel.html#object.__eq__). – Aran-Fey Sep 28 '18 at 14:18
  • @chepner, If that's the case, perhaps they *all* do some kind of type conversion. Of course, I don't know individual implementations well enough to make such a claim. But if it's true, it's still useful to know :). – jpp Sep 28 '18 at 14:18
  • 1
    @Aran-Fey At the language level, they're equivalent. You still have to *run* `a.__eq__(b)` before you can find out if a call to `b.__eq__(a)` is required. Lexically, the translation is sound. Semantically, it's up to the implementation. (I could imagine an implementation where `int.__eq__` compares the two values itself, and I can also imagine an implementation where `int.__eq__` raises an exception and delegates the comparison to `float.__eq__`.) – chepner Sep 28 '18 at 14:24
  • 1
    @jpp *Python* doesn't do type conversions. Any particular *implementation* of Python might. – chepner Sep 28 '18 at 14:25
  • @chepner, That was patently clear in my previous comment. It may be useful for OP to delve further, e.g. the [CPython implementation](https://stackoverflow.com/a/30100743/9209546). – jpp Sep 28 '18 at 14:31
  • If the OP mentioned a specific implementation in the question, I'd address that, but he didn't. (And yes, most beginners may not differentiate between the language and CPython; that doesn't mean I can't.) – chepner Sep 28 '18 at 14:37
4

Objects of different types, except different numeric types, never compare equal.

And:

Python fully supports mixed arithmetic: when a binary arithmetic operator has operands of different numeric types, the operand with the “narrower” type is widened to that of the other, where integer is narrower than floating point, which is narrower than complex. Comparisons between numbers of mixed type use the same rule.

https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex

The comparison logic is implemented by each type's __eq__ method. And the standard numeric types are implemented in a way that they support comparisons (and arithmetic operations) among each other. Python as a language never does implicit type conversion (like Javascript's == operator would do implicit type juggling).

deceze
  • 510,633
  • 85
  • 743
  • 889
  • `==` is not a binary arithmetic operator, as described by Python documentation. Per the page you link to, it is a comparison operation. – Eric Postpischil Sep 28 '18 at 14:31
  • True, but *"Comparisons between numbers of mixed type use the same rule."* I've added a more apropos quote to the top nonetheless. – deceze Sep 28 '18 at 14:37
1

The simple answer is that the langue is designed this way. Here is an excerpt from the documentation supporting this:

6.10.1 Value Comparisons

Numbers of built-in numeric types (Numeric Types — int, float, complex) and of the standard library types fractions.Fraction and decimal.Decimal can be compared within and across their types, with the restriction that complex numbers do not support order comparison.

In other words, we want different numeric types with the same value to be equal.

PEP 20

Special cases aren't special enough to break the rules.

Although practicality beats purity.

What benefit is there to making numeric types not comparable, besides making life difficult in most common cases?

1

You can have a look at the source code for the CPython implementation.

The function is preceded by this comment explaining how the conversion is attempted:

/* Comparison is pretty much a nightmare.  When comparing float to float,
 * we do it as straightforwardly (and long-windedly) as conceivable, so
 * that, e.g., Python x == y delivers the same result as the platform
 * C x == y when x and/or y is a NaN.
 * When mixing float with an integer type, there's no good *uniform* approach.
 * Converting the double to an integer obviously doesn't work, since we
 * may lose info from fractional bits.  Converting the integer to a double
 * also has two failure modes:  (1) an int may trigger overflow (too
 * large to fit in the dynamic range of a C double); (2) even a C long may have
 * more bits than fit in a C double (e.g., on a 64-bit box long may have
 * 63 bits of precision, but a C double probably has only 53), and then
 * we can falsely claim equality when low-order integer bits are lost by
 * coercion to double.  So this part is painful too.
 */

Other implementations are not guaranteed to follow the same logic.

jpp
  • 159,742
  • 34
  • 281
  • 339
Thierry Lathuille
  • 23,663
  • 10
  • 44
  • 50
  • 3
    This comment is rather about the specific implementation details and nightmares therein needed to get the desired behaviour. It does not explain why that behaviour is desired in the first place. – deceze Sep 28 '18 at 15:37
0

From the documentation:

Python fully supports mixed arithmetic: when a binary arithmetic operator has operands of different numeric types, the operand with the “narrower” type is widened to that of the other, where plain integer is narrower than long integer is narrower than floating point is narrower than complex. Comparisons between numbers of mixed type use the same rule.

According to this 5*2 is widened to 10.0 and which is equal to 10.0 If you are comparing the mixed data types then the result will be considered on the basics of data type which is having long range, so in your case float range is more then int float max number can be --> 1.7976931348623157e+308 int max number can be --> 9223372036854775807

Thanks

Mark Dickinson
  • 29,088
  • 9
  • 83
  • 120
devender
  • 90
  • 8
  • `==` is not a binary arithmetic operator, as described by Python documentation. Per [this page](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex), it is a comparison operation. – Eric Postpischil Sep 28 '18 at 14:33
  • Hi Eric, Thanks for the correction but it will follows the same rule with comparison operator also. eg:- suppose binary operator 4.0/2 will give you 2.0 and comparison operator 4.0 == 4 will give you true, but the only difference here is you can see the o/p of 4.0/2 is converted to float but in 4.0 == 4 you can not see at o/p (as you get the Boolean result) but internally it is doing he same. – devender Sep 28 '18 at 14:47
  • 1
    Another answer pointed to [source code](https://github.com/python/cpython/blob/master/Objects/floatobject.c#L332) that shows us the approach of simply converting one type to another does not fulfill the specified requirements for comparison. Converting floating-point to wider integer cannot work because `3.25 == 3` would falsely return true because the conversion would drop the .25. Converting integer to floating-point has failures due to loss of precision. What is your basis (*e.g.*, documentation) for asserting that Python actually implements `==` by performing the “widening” you state? – Eric Postpischil Sep 28 '18 at 15:24
  • The "Comparisons between numbers of mixed type use the same rule." line from the docs is unfortunate and misleading, as the other answers explain. There's a (very) long-standing issue open to fix it: https://bugs.python.org/issue12067 – Mark Dickinson Sep 28 '18 at 16:59
-3

The == operator compares only the values but not the types. You can use the 'is' keyword to achieve the same effect as using === in other languages. For instance

5 is 5.0

returns False

  • 2
    [`is` is not equivalent to `===`!](https://stackoverflow.com/q/306313/476) `a = 12345; a is 12345 # false` – deceze Sep 28 '18 at 14:27
-4
== 

is a comparison operator

You are actually asking the interpreter if both sides of your expression are equal or not.

In other words you are asking for it to return a Boolean value, not to convert data types. If you want to convert the data types you will have to do so implicitly in your code.

  • Why the downvotes? Per [Python documentation](https://docs.python.org/3/reference/expressions.html#value-comparisons), “Numbers … can be compared within **and across** their types… they compare mathematically (algorithmically) correct…” Thus `==` is defined in terms of the mathematics—are both sides equal. That documentation does not say types are converted. It specifies the behavior in terms of mathematical comparison. – Eric Postpischil Sep 28 '18 at 14:28
  • 1
    I didn't down vote, but you are answering (correctly) the question stated in the body while ignoring the title. – chepner Sep 28 '18 at 14:36