-1

If I run this code in Python 3.8.6 I get the output False:

(((3980 - 91 + 1)/(3980)) * ((1 / 3980)**(91 - 1))) > (((3981 - 91 + 1)/(3981)) * ((1 / 3981)**(91 - 1)))

However, if I run this in WolframAlpha it returns True.

I'm guessing this happens due to float imprecision. But why does it happen exactly here? Out of 12 million combinations of s and z that I've tested for the following function this only happens twice. z = 91 with s = 3980 and z = 92 with s = 3457.

(((s - z + 1)/(s)) * ((1 / s)**(z - 1))) > ((((s+1) - z + 1)/(s+1)) * ((1 / (s+1))**(z - 1)))

Also, try these in your interpreter:

  1. (((3458 - 92 + 1)/(3458)) * ((1 / 3458)**(92 - 1))) > (((3459 - 92 + 1)/(3459)) * ((1 / 3459)**(92 - 1)))
  2. (((3457 - 92 + 1)/(3457)) * ((1 / 3457)**(92 - 1))) > (((3458 - 92 + 1)/(3458)) * ((1 / 3458)**(92 - 1)))
  3. (((3456 - 92 + 1)/(3456)) * ((1 / 3456)**(92 - 1))) > (((3457 - 92 + 1)/(3457)) * ((1 / 3457)**(92 - 1)))

They give these results:

  1. True
  2. False
  3. True

Why does the pattern True False True happen at only these inputs? Holding z constant, no other value of s returns False.

Connor
  • 4,216
  • 2
  • 29
  • 40
  • 1
    It is really not clear what kind of answer you are expecting. "But why does it happen exactly here?" has several answers, from deeply philosophical to floating point math, down to "because you've arbitrarily chosen this unwieldy formula that happens to have these two odd points". – MisterMiyagi Feb 07 '21 at 20:09
  • The raw formula and filled-in formula shown do not seem to match. The trailing term ``((1 / s+1)**(z - 1))`` is equivalent to ``(((1 / s) + 1)**(z - 1))`` not ``((1 / (s+1))**(z - 1))``; for ``s, z = 3980, 91`` that is ``(((1 / 3980) + 1)**(91 - 1)) == 1.0228677735155611`` not ``((1 / 3981)**(91 - 1)) == 0.0``. Which of these formulas as you asking about, and which have you probed? – MisterMiyagi Feb 07 '21 at 20:28
  • @MisterMiyagi You're totally correct, thank you for pointing that out. I fixed the formula, it was really just being used in an f-string format, so the order of precedent would be correct there. – Connor Feb 07 '21 at 20:32
  • @MisterMiyagi I want to know why the `True False True` pattern happens with these inputs. It doesn't happen anywhere else. – Connor Feb 07 '21 at 20:37
  • FYI WolframAlpha looks like it uses Fractions and has more precision. There is an "Exact Form" button that lists the difference as a fraction. – Mark Tolonen Feb 07 '21 at 21:34

2 Answers2

3

Rounding error. If you want to know precisely go read the IEEE 754 specification. Your numbers are so small they are in the subnormal range and have 5-10 bits of precision in your examples. Floats in the normal range have 53 bits of precision.


If you look at the two values using float.hex() it shows their exact binary representation:

>>> (((3980 - 91 + 1)/(3980)) * ((1 / 3980)**(91 - 1))).hex()
'0x0.0p+0'
>>> (((3981 - 91 + 1)/(3981)) * ((1 / 3981)**(91 - 1))).hex()
'0x0.0p+0'

They are both so small at IEEE 754 float64 precision they are both rounded to zero. If fact the last term is really small:

>>> ((1/3981)**89).hex()
'0x0.0000000000327p-1022'
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
1

Mark Tolonen answered your question and if you need more precision, use fractions:

>>> from fractions import Fraction
>>> (Fraction(3980 - 91 + 1, 3980) * Fraction(1, 3980) ** (91 - 1)) > (Fraction(3981 - 91 + 1, 3981) * Fraction(1, 3981) ** (91 - 1))
True
HTF
  • 6,632
  • 6
  • 30
  • 49