Python Infinity - Any caveats?
Asked Answered
S

5

184

So Python has positive and negative infinity:

float("inf"), float("-inf")

This just seems like the type of feature that has to have some caveat. Is there anything I should be aware of?

Sidecar answered 27/10, 2009 at 0:12 Comment(1)
Note that the constant 1e309 will be interpreted as +inf and -1e309 will be interpreted as -inf.Jemmie
L
98

You can still get not-a-number (NaN) values from simple arithmetic involving inf:

>>> 0 * float("inf")
nan

Note that you will normally not get an inf value through usual arithmetic calculations:

>>> 2.0**2
4.0
>>> _**2
16.0
>>> _**2
256.0
>>> _**2
65536.0
>>> _**2
4294967296.0
>>> _**2
1.8446744073709552e+19
>>> _**2
3.4028236692093846e+38
>>> _**2
1.157920892373162e+77
>>> _**2
1.3407807929942597e+154
>>> _**2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
OverflowError: (34, 'Numerical result out of range')

The inf value is considered a very special value with unusual semantics, so it's better to know about an OverflowError straight away through an exception, rather than having an inf value silently injected into your calculations.

Lefkowitz answered 27/10, 2009 at 0:31 Comment(4)
A simple float addition, multiplication, etc will happily produce inf though: f=1.3407807929942597e+154; f*f => inf. It seems rather an exception of ** to raise an OverflowError.Counterreply
@eregon, actually, ** seems a tad buggy. When it overflows with real numbers, it throws an error, but when any of its operands is inf or -inf, it returns either 0.0 or inf. So it does work correctly when the input is inifinty, but not when the result should be infinity.Ethelda
@Ethelda That is not buggy. Overflowing means the number is very large. Too large to represent it, but still far smaller than infinity. Putting infinity into such a place may be useful for the exception handler of your particular application logic, but would be incorrect for Python in general.Afterdamp
@Lutz if it comes up as multiplication then, it's still inconsistent behavior. Certainly big*big is not infinity, either.Airflow
E
105

Python's implementation follows the IEEE-754 standard pretty well, which you can use as a guidance, but it relies on the underlying system it was compiled on, so platform differences may occur. Recently¹, a fix has been applied that allows "infinity" as well as "inf", but that's of minor importance here.

The following sections equally well apply to any language that implements IEEE floating point arithmetic correctly, it is not specific to just Python.

Comparison for inequality

When dealing with infinity and greater-than > or less-than < operators, the following counts:

  • any number including +inf is higher than -inf
  • any number including -inf is lower than +inf
  • +inf is neither higher nor lower than +inf
  • -inf is neither higher nor lower than -inf
  • any comparison involving NaN is false (inf is neither higher, nor lower than NaN)

Comparison for equality

When compared for equality, +inf and +inf are equal, as are -inf and -inf. This is a much debated issue and may sound controversial to you, but it's in the IEEE standard and Python behaves just like that.

Of course, +inf is unequal to -inf and everything, including NaN itself, is unequal to NaN.

Calculations with infinity

Most calculations with infinity will yield infinity, unless both operands are infinity, when the operation division or modulo, or with multiplication with zero, there are some special rules to keep in mind:

  • when multiplied by zero, for which the result is undefined, it yields NaN

  • when dividing any number (except infinity itself) by infinity, which yields 0.0 or -0.0².

  • when dividing (including modulo) positive or negative infinity by positive or negative infinity, the result is undefined, so NaN.

  • when subtracting, the results may be surprising, but follow common math sense:

  • when doing inf - inf, the result is undefined: NaN;

  • when doing inf - -inf, the result is inf;

  • when doing -inf - inf, the result is -inf;

  • when doing -inf - -inf, the result is undefined: NaN.

  • when adding, it can be similarly surprising too:

  • when doing inf + inf, the result is inf;

  • when doing inf + -inf, the result is undefined: NaN;

  • when doing -inf + inf, the result is undefined: NaN;

  • when doing -inf + -inf, the result is -inf.

  • using math.pow, pow or ** is tricky, as it doesn't behave as it should. It throws an overflow exception when the result with two real numbers is too high to fit a double precision float (it should return infinity), but when the input is inf or -inf, it behaves correctly and returns either inf or 0.0. When the second argument is NaN, it returns NaN, unless the first argument is 1.0. There are more issues, not all covered in the docs.

  • math.exp suffers the same issues as math.pow. A solution to fix this for overflow is to use code similar to this:

      try:
          res = math.exp(420000)
      except OverflowError:
          res = float('inf')
    

Notes

Note 1: as an additional caveat, that as defined by the IEEE standard, if your calculation result under-or overflows, the result will not be an under- or overflow error, but positive or negative infinity: 1e308 * 10.0 yields inf.

Note 2: because any calculation with NaN returns NaN and any comparison to NaN, including NaN itself is false, you should use the math.isnan function to determine if a number is indeed NaN.

Note 3: though Python supports writing float('-NaN'), the sign is ignored, because there exists no sign on NaN internally. If you divide -inf / +inf, the result is NaN, not -NaN (there is no such thing).

Note 4: be careful to rely on any of the above, as Python relies on the C or Java library it was compiled for and not all underlying systems implement all this behavior correctly. If you want to be sure, test for infinity prior to doing your calculations.


¹) Recently means since version 3.2.
²) Floating points support positive and negative zero, so: x / float('inf') keeps its sign and -1 / float('inf') yields -0.0, 1 / float(-inf) yields -0.0, 1 / float('inf') yields 0.0 and -1/ float(-inf) yields 0.0. In addition, 0.0 == -0.0 is true, you have to manually check the sign if you don't want it to be true.

Ethelda answered 27/10, 2009 at 0:33 Comment(10)
A small nitpick: not every calculation with infinity yields infinity: -1 * float('infinity') == -infVastah
@Eval: I'm not sure what you mean, as the example calculation you give yields infinity and in my post I wrote about the only exception I know of: multiplication with zero.Ethelda
I'd say -inf != inf, much like -1 != 1.Vastah
@Evan: yes of course, -inf != inf. But my point was, either are infinity and every calculation with them yields infinity (or NaN). Maybe I should've made that more clear in my post.Ethelda
That's why I said it was a small nitpick. You had me worried for a minute that sign would be totally ignored when working with infinity, and I wanted to clarify for other people.Vastah
Well, nearly: 1 / float('infinity') == 0.0Uriel
@Phil: Although I am pretty sure you were just trying to show that not all calculations with inf result in inf or NaN, I just wanted to make it clear to others that may be reading through the comments, that 1/float('infinity')==0.0 is true; for, as you are approaching infinity, the result of the division approaches 0. I know it's just basic calculus, but I wanted to be sure those reading understand, or at least have a clue as to why, the result is what it is.Zahara
I have the feeling that this answer is much better than the accepted answer.Shabuoth
@Chris, thank you, but only the op has the power to change the accepted answer, provided he agrees ;)Ethelda
Although -0.0 == 0.0, -0.0 is not 0.0, and all integers in Python which have the same value are the same object. (Don't know if this is per spec., but it is true in CPython/PyPy)Kreutzer
L
98

You can still get not-a-number (NaN) values from simple arithmetic involving inf:

>>> 0 * float("inf")
nan

Note that you will normally not get an inf value through usual arithmetic calculations:

>>> 2.0**2
4.0
>>> _**2
16.0
>>> _**2
256.0
>>> _**2
65536.0
>>> _**2
4294967296.0
>>> _**2
1.8446744073709552e+19
>>> _**2
3.4028236692093846e+38
>>> _**2
1.157920892373162e+77
>>> _**2
1.3407807929942597e+154
>>> _**2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
OverflowError: (34, 'Numerical result out of range')

The inf value is considered a very special value with unusual semantics, so it's better to know about an OverflowError straight away through an exception, rather than having an inf value silently injected into your calculations.

Lefkowitz answered 27/10, 2009 at 0:31 Comment(4)
A simple float addition, multiplication, etc will happily produce inf though: f=1.3407807929942597e+154; f*f => inf. It seems rather an exception of ** to raise an OverflowError.Counterreply
@eregon, actually, ** seems a tad buggy. When it overflows with real numbers, it throws an error, but when any of its operands is inf or -inf, it returns either 0.0 or inf. So it does work correctly when the input is inifinty, but not when the result should be infinity.Ethelda
@Ethelda That is not buggy. Overflowing means the number is very large. Too large to represent it, but still far smaller than infinity. Putting infinity into such a place may be useful for the exception handler of your particular application logic, but would be incorrect for Python in general.Afterdamp
@Lutz if it comes up as multiplication then, it's still inconsistent behavior. Certainly big*big is not infinity, either.Airflow
A
3

So does C99.

The IEEE 754 floating point representation used by all modern processors has several special bit patterns reserved for positive infinity (sign=0, exp=~0, frac=0), negative infinity (sign=1, exp=~0, frac=0), and many NaN (Not a Number: exp=~0, frac≠0).

All you need to worry about: some arithmetic may cause floating point exceptions/traps, but those aren't limited to only these "interesting" constants.

Armillda answered 27/10, 2009 at 0:25 Comment(2)
So if my arithmetic too large it might become an inf?Sidecar
@Sidecar No, it will cause an OverflowError.Leekgreen
H
3

I found a caveat that no one so far has mentioned. I don't know if it will come up often in practical situations, but here it is for the sake of completeness.

Usually, calculating a number modulo infinity returns itself as a float, but a fraction modulo infinity returns nan (not a number). Here is an example:

>>> from fractions import Fraction
>>> from math import inf
>>> 3 % inf
3.0
>>> 3.5 % inf
3.5
>>> Fraction('1/3') % inf
nan

I filed an issue on the Python bug tracker. It can be seen at https://bugs.python.org/issue32968.

Update: this will be fixed in Python 3.8.

Halbeib answered 28/2, 2018 at 4:7 Comment(0)
R
3

A VERY BAD CAVEAT : Division by Zero

in a 1/x fraction, up to x = 1e-323 it is inf but when x = 1e-324 or little it throws ZeroDivisionError

>>> 1/1e-323
inf

>>> 1/1e-324
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: float division by zero

so be cautious!

Redmond answered 10/10, 2019 at 15:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.