math.nroot [was Re: A brief question.]
Steven D'Aprano
steve at REMOVETHIScyber.com.au
Sat Jul 2 23:37:26 EDT 2005
On Sun, 03 Jul 2005 02:22:23 +0200, Fredrik Johansson wrote:
> On 7/3/05, Tom Anderson <twic at urchin.earth.li> wrote:
>> That's one way. I'd do:
>>
>> root = value ** 0.5
>>
>> Does that mean we can expect Guido to drop math.sqrt in py3k? :)
>
> I'd rather like to see a well implemented math.nthroot. 64**(1/3.0)
> gives 3.9999999999999996, and this error could be avoided.
py> 64**(1/3.0)
3.9999999999999996
Use the fact that if z = x**y, log(z) = y*log(x).
py> import math
py> math.exp(1/3.0*math.log(64))
3.9999999999999991
That's even less accurate.
py> math.exp(math.log(64)/3.0)
4.0
Success!!!
Another (random-ish) example:
py> 17**5
1419857
py> 1419857**(1.0/5)
17.000000000000004
py> math.exp(math.log(1419857)/5.0)
17.0
def nroot(x, y):
"""Returns the yth root of x.
More accurate than using x**(1/y).
Handles most, but not all, edge cases correctly. (Does not check for
y as either negative zero or negative infinity.)
"""
if x < 0.0:
raise ValueError("Complex valued result.")
if y == 0.0: # x**inf
if x > 1.0:
try:
return float("inf")
except:
try:
return 1.0/0.0
except:
raise ValueError("Overflow")
elif x == 1.0: # 1**N = 1 for any positive N
return 1.0
else: # 0**N = 0 for any non-zero N
return 0.0
# check the limiting case where y is positive infinity
if 1.0/y == 0.0:
# x**0 = 1 for all real x (including the limiting case x = 0)
return 1.0
if x == 0.0: # 0**N = 0 for any non-zero N
return 0.0
else:
return math.exp(math.log(x)/y)
Note how much simpler this would be if we could guarantee proper
infinities and NaNs in the code. We could turn a 23-line block to a
one-liner.
--
Steven.
More information about the Python-list
mailing list