math.nroot [was Re: A brief question.]

Steven D'Aprano steve at REMOVETHIScyber.com.au
Sun Jul 3 19:25:13 CEST 2005


On Sun, 03 Jul 2005 15:46:35 +0100, Tom Anderson wrote:

> I think there would be a lot less confusion over the alleged inaccuracy of 
> floating point if everyone wrote in hex - indeed, i believe that C99 has 
> hex floating-point literals. C has always been such a forward-thinking 
> language!

No, all that would do is shift the complaint from "Python has a bug when
you divide 10 into 1.0" to "Python has a bug when you convert 0.1 into hex".

[snip]
>> But this works:
>>
>> py> inf = float("inf")
>> py> inf
>> inf
> 
> True. Still, i'd rather not have to rely on string parsing to generate a 
> fairly fundamental arithmetic quantity.

What happens when your Python script is running on a platform that can
deal with 1e300*1e300 giving 1e600 without overflow? Okay, maybe no such
platform exists today (or does it?), but it could exist, and your code
will fail on those systems.

I believe that the IEEE standard specifies that float("inf") should give
an infinity, just as float("0.0") should give a zero.

For production code, I'd wrap float("inf") in a try...except and only
fall back on your method if it raised an exception, and then I'd actually
test that your result was a real inf (eg by testing that inf+inf=inf).

[snip]

>>> The IEEE spec actually says that (x == nan) should be *false* for every x,
>>> including nan. I'm not sure if this is more or less stupid than what
>>> python does!
>>
>> Well, no, the IEEE standard is correct. NaNs aren't equal to anything, 
>> including themselves, since they aren't numbers.
> 
> I don't buy that. Just because something isn't a number doesn't mean it 
> can't be equal to something else, does it? I mean, we even say x == None 
> if x is indeed None.

Yes, but None does equal None, since there is only one None, and by
definition, a thing is equal to itself.

But NaNs are _not_ things. That is the whole point! Yes, we _represent_
INF-INF as a particular bit-pattern and call it NaN, but mathematically
subtracting infinity from infinity is not defined. There is no answer to
the question "what is infinity subtracted from infinity?".

We pretend that the answer is NaN, but that isn't right. The NaN is just
there as a placeholder for "there is no answer", so that we don't have
to sprinkle our code with a thousand and one tests.

Since INF-INF doesn't have an answer, we can't do this:

x = inf - inf
y = inf - inf

and expect that x == y. 


> Moreover, this freaky rule about NaNs means that this 
> is an exception to the otherwise absolutely inviolate law that x == x.

Yes. So what? Remove the NaN shorthand:

"The non-existent answer to this question is the same non-existent answer
to this other question."

It doesn't make sense to say that a non-thing is equal to anything -- even
to itself, since itself doesn't exist.

(Remember, a NaN is just an illusionary placeholder, not a number.)

> I'd rather have that simple, fundamental logical consistency than some IEEE 
> rocket scientist's practical-value-free idea of mathematical consistency.

Ah, from a _programmer's_ perspective, there is an argument that the
simplicity of just testing NaNs with equality outweighs the logical
silliness of doing such a thing. But, apart from testing whether a float
is a NaN, why would you ever want to do an equality test?

The only usage case I can think of is would be something like this:

def isNaN(x):
    return x == SOME_KNOWN_NAN

But that won't work, because there are lots of different NaNs. 254 of
them, or twice that if you include signed NaNs (which you shouldn't, but
you do have to allow for them in equality testing).

Any good IEEE compliant system should already have a built-in function
that tests for NaNs.


[snip]

>>> And while i'm ranting, how come these expressions aren't the same:
>>>
>>> 1e300 * 1e300
>>> 1e300 ** 2
>>
>> Because this is floating point, not real maths :-)
>>
>> I get inf and Overflow respectively. What do you get?
> 
> The same. They really ought to give the same answer.

In real numbers, yes they should. In floating point, that is too much to
expect. In mathematics, the order you do your calculation shouldn't
matter. But in floating point, where there is rounding errors and finite
precision issues, it does.

The aim of the High Level Language designer is to insulate the programmer
from the ickiness of actual hardware implementation. Unfortunately, in the
case of floating point, you can't get away from the hardware limitations.


>>> And finally, does Guido know something about arithmetic that i don't,
>>> or is this expression:
>>>
>>> -1.0 ** 0.5
>>>
>>> Evaluated wrongly?
>>
>> No, it is evaluated according to the rules of precedence. It is
>> equivalent to -(1.0**0.5). You are confusing it with (-1.0)**0.5 which
>> fails as expected.
> 
> Ah. My mistake. I submit that this is also a bug in python's grammar.
> There's probably some terribly good reason for it, though.

Yes. You generally want exponentiation to have the highest precedence.

2*3**4 should give 162, not 1296. Think about how you would write
that mathematically, with pencil and paper: the 4 is written as a
superscript over the 3, and is applied to that before multiplying by the 2.

Unary minus is equivalent to multiplying by -1, so -3**4 is equivalent to
-1*3**4.

These are the rules of precedence mathematicians have worked out over many
centuries. Yes, some of them are arbitrary choices, but they do work, and
changing them for another arbitrary choice doesn't give us any benefit.


-- 
Steven.




More information about the Python-list mailing list