Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #6601 > unrolled thread

Re: float("nan") in set or as key

Started byCarl Banks <pavlovevidence@gmail.com>
First post2011-05-29 17:55 -0700
Last post2011-05-30 23:22 +0000
Articles 11 — 5 participants

Back to article view | Back to comp.lang.python


Contents

  Re: float("nan") in set or as key Carl Banks <pavlovevidence@gmail.com> - 2011-05-29 17:55 -0700
    Re: float("nan") in set or as key Chris Angelico <rosuav@gmail.com> - 2011-05-30 11:14 +1000
      Re: float("nan") in set or as key Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-05-30 04:15 +0000
        Re: float("nan") in set or as key John Nagle <nagle@animats.com> - 2011-05-29 21:25 -0700
        Re: float("nan") in set or as key Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-05-30 06:14 +0000
    Re: float("nan") in set or as key Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-05-30 03:59 +0000
      Re: float("nan") in set or as key Chris Torek <nospam@torek.net> - 2011-05-30 04:29 +0000
        Re: float("nan") in set or as key Chris Torek <nospam@torek.net> - 2011-05-30 05:53 +0000
        Re: float("nan") in set or as key Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-05-30 06:13 +0000
          Re: float("nan") in set or as key Chris Torek <nospam@torek.net> - 2011-05-30 19:58 +0000
            Re: float("nan") in set or as key Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2011-05-30 23:22 +0000

#6601 — Re: float("nan") in set or as key

FromCarl Banks <pavlovevidence@gmail.com>
Date2011-05-29 17:55 -0700
SubjectRe: float("nan") in set or as key
Message-ID<271acea3-c38f-4123-9038-e348fb841971@glegroupsg2000goo.googlegroups.com>
On Sunday, May 29, 2011 4:31:19 PM UTC-7, Steven D&#39;Aprano wrote:
> On Sun, 29 May 2011 22:19:49 +0100, Nobody wrote:
> 
> > On Sun, 29 May 2011 10:29:28 +0000, Steven D'Aprano wrote:
> > 
> >>>     The correct answer to "nan == nan" is to raise an exception,
> >>>     because
> >>> you have asked a question for which the answer is nether True nor
> >>> False.
> >> 
> >> Wrong.
> > 
> > That's overstating it. There's a good argument to be made for raising an
> > exception. 
> 
> If so, I've never heard it, and I cannot imagine what such a good 
> argument would be. Please give it.

Floating point arithmetic evolved more or less on languages like Fortran where things like exceptions were unheard of, and defining NaN != NaN was a bad trick they chose for testing against NaN for lack of a better way.

If exceptions had commonly existed in that environment there's no chance they would have chosen that behavior; comparison against NaN (or any operation with NaN) would have signaled a floating point exception.  That is the correct way to handle exceptional conditions.

The only reason to keep NaN's current behavior is to adhere to IEEE, but given that Python has trailblazed a path of correcting arcane mathematical behavior, I definitely see an argument that Python should do the same for NaN, and if it were done Python would be a better language.


Carl Banks

[toc] | [next] | [standalone]


#6604

FromChris Angelico <rosuav@gmail.com>
Date2011-05-30 11:14 +1000
Message-ID<mailman.2245.1306718101.9059.python-list@python.org>
In reply to#6601
On Mon, May 30, 2011 at 10:55 AM, Carl Banks <pavlovevidence@gmail.com> wrote:
> If exceptions had commonly existed in that environment there's no chance they would have chosen that behavior; comparison against NaN (or any operation with NaN) would have signaled a floating point exception.  That is the correct way to handle exceptional conditions.
>
> The only reason to keep NaN's current behavior is to adhere to IEEE, but given that Python has trailblazed a path of correcting arcane mathematical behavior, I definitely see an argument that Python should do the same for NaN, and if it were done Python would be a better language.

If you're going to change behaviour, why have a floating point value
called "nan" at all? Other than being a title for one's grandmother,
what meaning does that string have, and why should it be able to be
cast as floating point?

Lifting from http://en.wikipedia.org/wiki/NaN a list of things that
can return a NaN (I've removed non-ASCII characters from this
snippet):
* Operations with a NaN as at least one operand.
(you need to bootstrap that somehow, so we can ignore this - it just
means that nan+1 = nan)

* The divisions 0/0 and infinity/infinity
* The multiplications 0*infinity and infinity*0
* The additions +inf + (-inf), (-inf) + +inf and equivalent subtractions
* The standard pow function and the integer exponent pown function
define 0**0, 1**inf, and inf**0 as 1.
* The powr function define all three indeterminate forms as invalid
operations and so returns NaN.
* The square root of a negative number.
* The logarithm of a negative number
* The inverse sine or cosine of a number that is less than -1 or
greater than +1.

Rather than having comparisons with NaN trigger exceptions, wouldn't
it be much cleaner to have all these operations trigger exceptions?
And, I would guess that they probably already do.

NaN has an additional use in that it can be used like a "null
pointer"; a floating-point variable can store 1.0, or 0.000000000005,
or "no there's no value that I'm storing in this variable". Since a
Python variable can contain None instead of a float, this use is
unnecessary too.

So, apart from float("nan"), are there actually any places where real
production code has to handle NaN? I was unable to get a nan by any of
the above methods, except for operations involving inf; for instance,
float("inf")-float("inf") == nan. All the others raised an exception
rather than return nan.

Chris Angelico

[toc] | [prev] | [next] | [standalone]


#6619

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-05-30 04:15 +0000
Message-ID<4de319cf$0$29990$c3e8da3$5496439d@news.astraweb.com>
In reply to#6604
On Mon, 30 May 2011 11:14:58 +1000, Chris Angelico wrote:

> So, apart from float("nan"), are there actually any places where real
> production code has to handle NaN? I was unable to get a nan by any of
> the above methods, except for operations involving inf; for instance,
> float("inf")-float("inf") == nan. All the others raised an exception
> rather than return nan.

That's Python's poor design, due to reliance on C floating point 
libraries that have half-hearted support for IEEE-754, and the 
obstruction of people who don't understand the usefulness of NANs. They 
shouldn't raise unless the caller specifies that he wants exceptions. The 
default behaviour should be the most useful one, namely quiet 
(propagating) NANs, rather than halting the calculation because of 
something which may or may not be an error and may or may not be 
recoverable.

Even Apple's Hypertalk supported them better in the late 1980s than 
Python does now, and that was a language aimed at non-programmers!

The Decimal module is a good example of what floats should do. All flags 
are supported, so you can choose whether you want exceptions or NANs. I 
don't like Decimal's default settings, but at least they can be changed.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#6622

FromJohn Nagle <nagle@animats.com>
Date2011-05-29 21:25 -0700
Message-ID<4de31c20$0$2134$742ec2ed@news.sonic.net>
In reply to#6619
On 5/29/2011 9:15 PM, Steven D'Aprano wrote:
> On Mon, 30 May 2011 11:14:58 +1000, Chris Angelico wrote:
>
>> So, apart from float("nan"), are there actually any places where real
>> production code has to handle NaN?

    Yes.  I used to write dynamic simulation engines.  There were
situations that produced floating point overflow, leading to NaN
values.  This wasn't an error; it just meant that the timestep
had to be reduced to handle some heavy object near the moment of
first collision.

    Note that the difference between two INF values is a NaN.

    It's important that ordered comparisons involving NaN and INF
raise exceptions so that you don't lose an invalid value.  If
you're running with non-signaling NaNs, the idea is supposed to
be that, at the end of the computation, you check all your results
for INF and NaN values, to make sure you didn't overflow somewhere
during the computation.  If, within the computation, there are
branches based on ordered comparisons, and those don't raise an
exception when the comparison result is unordered, you can reach
the end of the computation with valid-looking but wrong values.

				John Nagle

[toc] | [prev] | [next] | [standalone]


#6630

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-05-30 06:14 +0000
Message-ID<4de335dd$0$29990$c3e8da3$5496439d@news.astraweb.com>
In reply to#6619
On Mon, 30 May 2011 04:15:11 +0000, Steven D'Aprano wrote:

> On Mon, 30 May 2011 11:14:58 +1000, Chris Angelico wrote:
> 
>> So, apart from float("nan"), are there actually any places where real
>> production code has to handle NaN? I was unable to get a nan by any of
>> the above methods, except for operations involving inf; for instance,
>> float("inf")-float("inf") == nan. All the others raised an exception
>> rather than return nan.
> 
> That's Python's poor design, due to reliance on C floating point
> libraries that have half-hearted support for IEEE-754, and the
> obstruction of people who don't understand the usefulness of NANs.

That last comment mine is a bit harsh, and I'd like to withdraw it as 
unnecessarily confrontational.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#6618

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-05-30 03:59 +0000
Message-ID<4de31635$0$29990$c3e8da3$5496439d@news.astraweb.com>
In reply to#6601
On Sun, 29 May 2011 17:55:22 -0700, Carl Banks wrote:

> Floating point arithmetic evolved more or less on languages like Fortran
> where things like exceptions were unheard of, 

I'm afraid that you are completely mistaken.

Fortran IV had support for floating point traps, which are "things like 
exceptions". That's as far back as 1966. I'd be shocked if earlier 
Fortrans didn't also have support for traps.

http://www.bitsavers.org/pdf/ibm/7040/C28-6806-1_7040ftnMathSubrs.pdf


The IEEE standard specifies that you should be able to control whether a 
calculation traps or returns a NAN. That's how Decimal does it, that's 
how Apple's (sadly long abandoned) SANE did it, and floats should do the 
same thing.

Serious numeric languages like Fortran have supported traps long before 
exceptions were invented.



> and defining NaN != NaN
> was a bad trick they chose for testing against NaN for lack of a better
> way.

That's also completely wrong. The correct way to test for a NAN is with 
the IEEE-mandated function isnan(). The NAN != NAN trick is exactly that, 
a trick, used by programmers when their language or compiler doesn't 
support isnan().

Without support for isinf(), identifying an INF is just as hard as 
identifying an NAN, and yet their behaviour under equality is the 
complete opposite:

>>> inf = float('inf')
>>> inf == inf
True


> If exceptions had commonly existed in that environment there's no chance
> they would have chosen that behavior; 

They did exist, and they did choose that behaviour.


> comparison against NaN (or any
> operation with NaN) would have signaled a floating point exception. 
> That is the correct way to handle exceptional conditions.
> 
> The only reason to keep NaN's current behavior is to adhere to IEEE, 

And because the IEEE behaviour is far more useful than the misguided 
reliance on exceptions for things which are not exceptional.

Before spreading any more misinformation about IEEE 754 and NANs, please 
learn something about it:

http://grouper.ieee.org/groups/754/
http://www.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps

I particularly draw your attention to the FAQ about NANs:

http://grouper.ieee.org/groups/754/faq.html#exceptions


[quote]
The 754 model encourages robust programs. It is intended not only for 
numerical analysts but also for spreadsheet users, database systems, or 
even coffee pots. The propagation rules for NaNs and infinities allow 
inconsequential exceptions to vanish. Similarly, gradual underflow 
maintains error properties over a precision's range. 

When exceptional situations need attention, they can be examined 
immediately via traps or at a convenient time via status flags. Traps can 
be used to stop a program, but unrecoverable situations are extremely 
rare. Simply stopping a program is not an option for embedded systems or 
network agents. More often, traps log diagnostic information or 
substitute valid results. 

Flags offer both predictable control flow and speed. Their use requires 
the programmer be aware of exceptional conditions, but flag stickiness 
allows programmers to delay handling exceptional conditions until 
necessary.
[end quote]



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#6624

FromChris Torek <nospam@torek.net>
Date2011-05-30 04:29 +0000
Message-ID<irv6ev01jht@news1.newsguy.com>
In reply to#6618
In article <4de31635$0$29990$c3e8da3$5496439d@news.astraweb.com>,
Steven D'Aprano  <steve+comp.lang.python@pearwood.info> wrote:
>That's also completely wrong. The correct way to test for a NAN is with 
>the IEEE-mandated function isnan(). The NAN != NAN trick is exactly that, 
>a trick, used by programmers when their language or compiler doesn't 
>support isnan().

Perhaps it would be reasonable to be able to do:

    x.isnan()

when x is a float.

>Without support for isinf(), identifying an INF is just as hard as 
>identifying an NAN, and yet their behaviour under equality is the 
>complete opposite:
>
>>>> inf = float('inf')
>>>> inf == inf
>True

Fortunately:

    def isnan(x):
        return x != x
    _inf = float("inf")
    def isinf(x):
        return x == _inf
    del _inf

both do the trick here.

I would like to have both modes (non-exception-ing and exception-ing)
of IEEE-style float available in Python, and am not too picky about
how they would be implemented or which one would be the default.
Python could also paper over the brokenness of various actual
implementations (where signalling vs quiet NaNs, and so on, do not
quite work right in all cases), with some performance penalty on
non-conformant hardware.
-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W)  +1 801 277 2603
email: gmail (figure it out)      http://web.torek.net/torek/index.html

[toc] | [prev] | [next] | [standalone]


#6628

FromChris Torek <nospam@torek.net>
Date2011-05-30 05:53 +0000
Message-ID<irvbct030b9@news2.newsguy.com>
In reply to#6624
In article <irv6ev01jht@news1.newsguy.com> I wrote, in part:
>    _inf = float("inf")
>    def isinf(x):
>        return x == _inf
>    del _inf

Oops, take out the del, or otherwise fix the obvious problem,
e.g., perhaps:

    def isinf(x):
        return x == isinf._inf
    isinf._inf = float("inf")

(Of course, if something like this were adopted properly, it would
all be in the base "float" type anyway.)
-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W)  +1 801 277 2603
email: gmail (figure it out)      http://web.torek.net/torek/index.html

[toc] | [prev] | [next] | [standalone]


#6629

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-05-30 06:13 +0000
Message-ID<4de3358b$0$29990$c3e8da3$5496439d@news.astraweb.com>
In reply to#6624
On Mon, 30 May 2011 04:29:19 +0000, Chris Torek wrote:

> In article <4de31635$0$29990$c3e8da3$5496439d@news.astraweb.com>, Steven
> D'Aprano  <steve+comp.lang.python@pearwood.info> wrote:
>>That's also completely wrong. The correct way to test for a NAN is with
>>the IEEE-mandated function isnan(). The NAN != NAN trick is exactly
>>that, a trick, used by programmers when their language or compiler
>>doesn't support isnan().
> 
> Perhaps it would be reasonable to be able to do:
> 
>     x.isnan()
> 
> when x is a float.

Better than a float method is a function which takes any number as 
argument:


>>> import math, fractions, decimal
>>> math.isnan(fractions.Fraction(2, 3))
False
>>> math.isnan(decimal.Decimal('nan'))
True


You can even handle complex NANs with the cmath module:

>>> import cmath
>>> cmath.isnan(complex(1, float('nan')))
True




-- 
Steven

[toc] | [prev] | [next] | [standalone]


#6670

FromChris Torek <nospam@torek.net>
Date2011-05-30 19:58 +0000
Message-ID<is0stb018r3@news2.newsguy.com>
In reply to#6629
In article <4de3358b$0$29990$c3e8da3$5496439d@news.astraweb.com>
Steven D'Aprano  <steve+comp.lang.python@pearwood.info> wrote:
>Better than a float method is a function which takes any number as 
>argument:
>
>>>> import math, fractions, decimal
>>>> math.isnan(fractions.Fraction(2, 3))
>False
>>>> math.isnan(decimal.Decimal('nan'))
>True

Ah, apparently someone's been using Larry Wall's time machine. :-)

I should have looked at documentation.  In my case, though:

    $ python
    Python 2.5.1 (r251:54863, Dec 16 2010, 14:12:43) 
    [GCC 4.0.1 (Apple Inc. build 5465)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import math
    >>> math.isnan
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'module' object has no attribute 'isnan'

>You can even handle complex NANs with the cmath module:
>
>>>> import cmath
>>>> cmath.isnan(complex(1, float('nan')))
>True

Would it be appropriate to have isnan() methods for Fraction,
Decimal, and complex, so that you do not need to worry about whether
to use math.isnan() vs cmath.isnan()?  (I almost never work with
complex numbers so am not sure if the "or" behavior -- cmath.isinf
and cmath.isnan return true if either real or complex part are
Infinity or NaN respectively -- is appropriate in algorithms that
might be working on any of these types of numbers.)

It might also be appropriate to have trivial always-False isinf and
isnan methods for integers.
-- 
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W)  +1 801 277 2603
email: gmail (figure it out)      http://web.torek.net/torek/index.html

[toc] | [prev] | [next] | [standalone]


#6678

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2011-05-30 23:22 +0000
Message-ID<4de42698$0$29996$c3e8da3$5496439d@news.astraweb.com>
In reply to#6670
On Mon, 30 May 2011 19:58:35 +0000, Chris Torek wrote:

> In article <4de3358b$0$29990$c3e8da3$5496439d@news.astraweb.com> Steven
> D'Aprano  <steve+comp.lang.python@pearwood.info> wrote:
>>Better than a float method is a function which takes any number as
>>argument:
>>
>>>>> import math, fractions, decimal
>>>>> math.isnan(fractions.Fraction(2, 3))
>>False
>>>>> math.isnan(decimal.Decimal('nan'))
>>True
> 
> Ah, apparently someone's been using Larry Wall's time machine. :-)

He has one too? Just like Guido van Rossum.

 
> I should have looked at documentation.  In my case, though:
> 
>     $ python
>     Python 2.5.1 (r251:54863, Dec 16 2010, 14:12:43) [GCC 4.0.1 (Apple
>     Inc. build 5465)] on darwin Type "help", "copyright", "credits" or
>     "license" for more information.


Python 2.5 is two major releases behind! I feel your pain, though, 
because 2.5 is the system python on my desktop as well. (And 2.4 is the 
system python on my server, ouch!)

You should consider installing 2.7 and/or 3.2 in parallel with the system 
python.


> Would it be appropriate to have isnan() methods for Fraction, Decimal,
> and complex, so that you do not need to worry about whether to use
> math.isnan() vs cmath.isnan()?

Probably not. From a purely object-oriented, Java-esque viewpoint, yes, 
all number types should support isnan and isinf methods, but Python uses 
a more mixed style, and a function that accepts multiple types is 
appropriate.

Unless you're using complex numbers, you don't need to care about complex 
numbers. *wink* Hence for "normal" numeric use, stick to the math module. 
If you do need complex numbers, cmath.isnan works perfectly fine with non-
complex arguments:

>>> cmath.isnan(42)
False
>>> cmath.isnan(float('nan'))
True


-- 
Steven

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web