Several wrong results of exponentations of infinities

I noticed, that Python evaluates many exponentiation with float infinities, to 1 or inf, where limit does actually not converge. It think it would be better, if Python evaluated these to nan.

M1=float("inf")
M2=float("-inf")
print("(-∞)**0.25 should be inf+infj, but python returns :",M2**(0.25))#https://www.wolframalpha.com/input/?i=lim+x-%3Einfinity+%28%28-x%29%5E0.25%29
print("(-∞)**0 should be nan. For example lim x->infinity ((-(x^x))^(1/x))=∞ , but python returns :",M2**0)#https://www.wolframalpha.com/input/?i=lim+x-%3Einfinity+%28%28-%28x%5Ex%29%29%5E%281%2Fx%29%29
print("(-2)**∞ should be nan,beacuse (-2)**x=2**x*cos(π*x)+1j*2**x*sin(π*x), but python returns :",(-2)**M1) #https://www.wolframalpha.com/input/?i=lim+x-%3Einfinity+%28%28-2%29%5E%28x%29%29
print("(-1)**∞ should be nan,beacuse (-1)**x=cos(π*x)+1j*sin(π*x), but python returns :",(-1)**M1)#https://www.wolframalpha.com/input/?i=lim+x-%3Einfinity+%28%28-1%29%5E%28x%29%29
print("(-1)**(-∞) should be nan,beacuse (-1)**x=cos(π*x)-1j*sin(π*x), but python returns :",(-1)**M2)#https://www.wolframalpha.com/input/?i=lim+x-%3Einfinity+%28%28-1%29%5E%28-x%29%29
print("0**0 should be nan, but python returns:",0**0)#https://www.wolframalpha.com/input/?i=0%5E0
print("∞**0 should be nan. For example lim x->infinity((x^x)^(1/x))=∞ , but python returns :",M1**0)#https://www.wolframalpha.com/input/?i=lim+x-%3Einfinity+%28%28x%5Ex%29%5E%281%2Fx%29%29
print("(-∞)**∞ should be nan. For example lim x->infinity((-x)^x)= lim x->infinity (x^x*e^(i*π*x)) , but python returns :",M2**M1)#https://www.wolframalpha.com/input/?i=lim+x-%3Einfinity%28%28-x%29%5Ex%29

If someone wants to argue, that float(“inf”)**0 should be 1, because 0 is ecxact value, not limit, then also float(“inf”)*0 should be 0, but Python returns nan.

Hi Olle,

Most floating point calculations in Python are done according to the

results of your platform’s C maths libraries, which should be using

IEEE-754 rules. (But sadly, sometimes compliance is less than perfect.)

In this case, the correct standard to compare with is not Wolfram Alpha,

but the IEEE-754 standard. Do you know its rules for exponents on

infinities?

The other complexity is that float**float operations may not

necessarily generate a complex value. Python is a little inconsistent in

that regard.

Sorry, but I find your code virtually unreadable by email, too compact

and squashed up, with strings and code and comments mixed in. Whitespace

is our friend! Let me see if I can reproduce what you have done.

>>> from math import inf

>>> (-inf)**0.25

inf

That result seems inexplicable to me.

>>> (-inf)**0

1.0

>>> (-inf)**inf

inf

Those results are, I am pretty sure, according to the IEEE-754 rule that

infinities represent a float which has overflowed, in other words

something arbitrarily big but finite. Not a true mathemetical

infinity.

Another IEEE-754 rule is that if a calculation involving x returns the

same value for every finite x, then it should return that same result

for nonfinite x too. This is why we have this:

>>> from math import nan

>>> nan**0

1.0

That same rule gives us:

>>> (-1.0)**inf

1.0

>>> (-1.0)**(-inf)

1.0

>>> inf**0

1.0

but oddly not this:

>>> (-1.0)**nan

nan

I expect that is a quirk of my platform and that other platforms may do

something different.

>>> 0.0**0

1.0

That is common enough that there is even a Wikipedia page about it:

Apologies, Discuss seems to have inserted blank lines between every line
of my reply. That was not intentional.

I don’t know why I’m apologising for Discuss’ poor handling of plain
text emails. Does anyone know how to report an issue with it?

Ok, I get it. floats are just for fast approximate results and their properties(min value,max value, and accuracy and results for operations involving nan and infinity) are dependent of computer you are using, not determined by Python standard. Then it is clear, that their properties can not be changed for python.

If we did not consider performance or implementation difficulty at all, then it would be best if infinities would act similar to that:

M1=float("inf")
M2=M1*5
M3=M1*6
print(M2/M1)#outputs 5
print(M3/M1)#outputs 6
M4=M1+7
print(M4-M1)#outputs 7

,but I understand, that this is very hard to implement and could be rather made in some module.

Other approximation to this ideal would be that
func(float(“inf”),float(“inf”)) return a if forall monotonically increasing unbounded functions mono_1 and mono_2 lim_{x to infinity}(f(mono_1(x),mono_2(x)))=a and nan otherwise. If float arithmetics were defined in Python standard, then this could be easily implemented for arithmetic operators.

See also the comments at the top of mathmodule.c:

Like all the other cases in @Olle7’s post, this case too follows IEEE 754 rules. The special cases are handily spelled out in detail in section 9.2.1 of the standard. A more accessible resource (since IEEE 754 isn’t freely available) is C99 Annex F, which has publicly accessible drafts that also spell out the special values, and for 99.9% of cases (including these ones) agree with IEEE 754.

pow is a messy beast, and a compromise of the various different use-cases that people want to use it for. In particular, integer exponents are handled specially. The rule here is that pow(-inf, y) retains the negative sign if y is an odd integer, and drops it if y is finite, nonzero, and not an odd integer.

This one also follows IEEE 754 rules, and should be consistent across platforms. CPython has special-case code handling all the corner cases, so there shouldn’t be too many platform-specific quirks lurking.

@Olle7 : None of this behaviour is arbitrary. This was all carefully thought out by the IEEE 754 committee. Luckily, Python can benefit directly from the wisdom of that committee, and doesn’t have to invent its own language-specific rules for these special cases.

1 Like

Nope, I was misremembering. This is actually one of the few places where C99 and IEEE 754 do disagree, for one specific corner case. C99 F.9.4.4 implies that pow(0.0, -inf) should raise, while IEEE 754-2008 section 9.2.1 says that it should return inf and not raise. The same distinction applies to pow(-0.0, -inf). With the special-case handling that we currently have in place in CPython, 0.0 ** -inf folllows IEEE 754 rules and returns inf, while math.pow(0.0, -inf) follows C99 and raises ValueError. We may want to fix that discrepancy.

FYI, Mark has opened bpo 44339 for fixing the discrepancies between math.pow(0.0, -inf) and 0.0**-inf.