2

In python 3.5, I was just playing around with comparison operators and came across this (seeming) oddity.

Is there a threshold of number of zeros after the decimal point past which the interpreter decides to not consider values relevant because of the inherent inaccuracy of floating point values?

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
jeremy radcliff
  • 1,049
  • 3
  • 11
  • 27
  • Precision is limited, yes. Not exactly in the number of *decimal* digits though, so if you look at it as though it is, the precision will appear to vary. – harold Dec 21 '16 at 20:42
  • @harold, but when you *give* python two values to compare, there are no calculations to make (as in I'm not asking python to add, subtract, etc...two floating point values), so why can't it figure out that 5 != 5.000000000000000001? – jeremy radcliff Dec 21 '16 at 20:43
  • 1
    @BilltheLizard, I think the question is different because the context is comparison operators, although it's obviously related. In this case I'm asking why python can't figure out that 5.0000000000000000001 is not 5, not why python returns approximations when computing FPV's. Not sure it was worth an instant downvote...(not saying it was you, but whoever it was it'd be nice to get an explanation). – jeremy radcliff Dec 21 '16 at 20:44
  • 3
    @jeremyradcliff well they're not strings, so clearly there's (implicitly) a parsing step. Since they become true floats, they become limited to that precision. – harold Dec 21 '16 at 20:47
  • 1
    @harold, thank you, that makes sense. – jeremy radcliff Dec 21 '16 at 20:56
  • 1
    If you're up for it, look into [`float_richcompare`](https://github.com/python/cpython/blob/master/Objects/floatobject.c#L344) he's responsible for this operation. I generally think this is a nice Q. – Dimitris Fasarakis Hilliard Dec 21 '16 at 21:00
  • 1
    @JimFasarakis-Hilliard, thank you. It's probably beyond my current skills to grasp all of what's in that function, but I think I can gain a lot by going through it slowly and trying nonetheless. – jeremy radcliff Dec 21 '16 at 21:09
  • @JimFasarakis-Hilliard I agree. Info could be posted as answer? – Ecko Dec 21 '16 at 21:09
  • 1
    @XamuelSchulman I wouldn't feel comfortable doing that. I'd like to see it explained, I'm trying to explain it to myself first and then I'll post an answer (If someone doesn't beat me to it :-) – Dimitris Fasarakis Hilliard Dec 21 '16 at 21:11
  • @JimFasarakis-Hilliard Of course. I meant just that at some point all this could be put together into an answer instead of a bunch of comments. – Ecko Dec 21 '16 at 21:13
  • @JimFasarakis-Hilliard: None of the stuff in `float_richcompare` is interesting in the context of this question, though; everything relevant happens in the source code->float conversion step. The code path for `float_richcompare` in the `some_float == some_other_float` case boils down to a C-level `==` check. – user2357112 Dec 21 '16 at 21:14
  • @user2357112 certain? care to explain in an answer? I was confident the cause was in the `richcompare`. – Dimitris Fasarakis Hilliard Dec 21 '16 at 21:15
  • ok, just did `float(5.0000000000000000000000001)` and `float(5.00001)` and got my answer :-) that's what I get for going straight to the operation – Dimitris Fasarakis Hilliard Dec 21 '16 at 21:17
  • No, the question is not different. Python can't figure out that 5.0000000000000000001 is not 5 *because* Python returns approximations when interpreting FPVs. Just set `x = 5.0000000000000000001` then look at the value of `x`. – Bill the Lizard Dec 21 '16 at 21:41

1 Answers1

2

In short, during the phase were parsing of input is done, Python needs to transform your input to a C double which can then be transformed to a Python float. Inputs with more than 16 decimal digits are going to get approximated, with 5.0000000000000001 getting approximated to 5.0:

>>> 5.0000000000000001 
5.0

As a result the comparison 5 == 5.0000000000000001 is going to succeed (5 is going to get transformed to a Python float equal to 5.0 in order for the comparison to take place).

For digits less than the aforementioned, the result (can be represented) and speaks for itself:

>>> 5.000000000000001
5.000000000000001

Yes, float_richcompare has --unfortunately-- nothing to do with this behavior as I thought in my original comment on the question. It all happens before it gets invoked.

Dimitris Fasarakis Hilliard
  • 150,925
  • 31
  • 268
  • 253
  • Thank you for writing a complete answer, this is very helpful; I had no idea python was going through the process of turning the input into a C double. – jeremy radcliff Dec 21 '16 at 23:20
  • Why "unfortunately"? You surely wouldn't want an equality comparison of floats to do some kind of fuzzy comparison: it would mess up transitivity of equality, and hence set and dict containment, etc. – Mark Dickinson Dec 22 '16 at 08:16
  • @MarkDickinson I initially though this was something `float_richcompare` did i.e I missed that ` 5.0000000000000001` is going to get approximated to `5.0` by `float_new`;. I thought that happened in `richcompare`. The "unfortunately" directs to my "swing and miss" comment under the question :-) – Dimitris Fasarakis Hilliard Dec 22 '16 at 11:08
  • Well, not *any* input with more than 16 digits: E.g., 5.0000000000000005 is not 5. – Rick Regan Dec 22 '16 at 13:36
  • @RickRegan the wording was ambiguous, updated that to better describe what I meant to say. – Dimitris Fasarakis Hilliard Dec 22 '16 at 13:42