homepage

This issue tracker has been migrated to GitHub , and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Min/max not symmetric in presence of NaN
Type: behavior Stage:
Components: Versions: Python 3.3
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: rhettinger Nosy List: Marco Sulla, alex, belopolsky, daniel.urban, mark.dickinson, pitrou, rhettinger
Priority: normal Keywords: gsoc

Created on 2011年05月03日 18:31 by belopolsky, last changed 2022年04月11日 14:57 by admin. This issue is now closed.

Messages (13)
msg135055 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011年05月03日 18:31
>>> nan = float('nan')
>>> min(nan, 5)
nan
>>> min(5, nan)
5
Good arguments can be made in favor of either result, but different value for min(x, y) depending on the order of arguments can hardly be justified.
"In the face of ambiguity, refuse the temptation to guess" suggests that min/max with NaN should be an error. See also issue 11949.
msg135056 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2011年05月03日 18:34
Not specific to NaNs:
>>> min({1}, {2})
{1}
>>> min({2}, {1})
{2}
msg135057 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2011年05月03日 18:41
Undefined ordering means just that.
msg135058 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011年05月03日 18:46
On Tue, May 3, 2011 at 2:41 PM, Raymond Hettinger
<report@bugs.python.org> wrote:
..
> Undefined ordering means just that.
Means what? Compare float behavior to
Decimal('1')
>>> Decimal(1).max(Decimal('nan'))
Decimal('1')
>>> max(Decimal('1'), Decimal('nan'))
Traceback (most recent call last):
 ..
decimal.InvalidOperation: comparison involving NaN
Raymond, you don't really need to stop the debate 4 minutes after the
bug has been reported.
msg135062 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2011年05月03日 19:21
The report is invalid because min/max make no guarantees about values without a total ordering.
Your other tracker item correctly focused on the behavior of float('NaN') itself, rather than on the behavior of everything else in the Python world that compares two values.
msg136121 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011年05月16日 18:59
rhettinger> Your other tracker item correctly focused on the behavior of
rhettinger> float('NaN') itself,
You closed issue11949 as well, so it won't help. I disagree that this issue would be resolved by resolving issue11949. Defining max(nan, x) and nan < x are two different issues. Quoting Kahan,
"""
Some familiar functions have yet to be defined for NaN . For instance max{x, y} should deliver the same result as max{y, x} but almost no implementations do that when x is NaN . There are good reasons to define max{NaN, 5} := max{5, NaN} := 5 though many would disagree.
"""
-- Lecture Notes on the Status of IEEE Standard 754 for Binary
Floating-Point Arithmetic by Prof. W. Kahan
http://www.cs.berkeley.edu/~wkahan/ieee754status/ieee754.ps
In the same lecture, Prof. Kahan states that nan < x must signal.
My ideal solution would be to make nan < x signal, keep naive implementation of builtin max() and provide symmetric float.max such that nan.max(x) = x.max(nan) = x (nan result would be a valid but less useful alternative.)
This will make float behavior closer to that of Decimal.
msg136467 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2011年05月21日 19:22
> Prof. Kahan states that nan < x must signal.
Would that be the sentence that starts "In the syntax of C ..." ?
msg136468 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2011年05月21日 19:32
> keep naive implementation of builtin max()
Agreed.
> provide symmetric float.max such that nan.max(x) = x.max(nan) = x (nan
> result would be a valid but less useful alternative.)
That might be viable (a math module function might also make sense here), though it feels a bit YAGNI to me. If we were going to add such a method, it should follow IEEE 754: nan.max(x) == x.max(n) == x, but also nan.min(x) == x.min(nan) == x, for finite x. (See section 5.3.1.)
msg136470 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011年05月21日 19:44
On Sat, May 21, 2011 at 3:22 PM, Mark Dickinson <report@bugs.python.org> wrote:
>
> Mark Dickinson <dickinsm@gmail.com> added the comment:
>
>> Prof. Kahan states that nan < x must signal.
>
> Would that be the sentence that starts "In the syntax of C ..." ?
This is just sophistry. If Python was more popular than C at the
time Prof. Kahan wrote this, he would write "in the syntax of Python."
(Not directly on-topic, but Python 3 seems to be moving towards C
spelling of operators. I, for one, miss the removal of easy to type
'<>' in favor of finger-twisting '!='.)
msg136473 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2011年05月21日 19:54
> This is just sophistry. If Python was more popular than C at the
> time Prof. Kahan wrote this, he would write "in the syntax of Python."
I doubt it. C has a standard that explicitly states that < must signal on comparison with NaNs. Python doesn't.
Alexander, I've read both these documents (Kahan's lecture notes and IEEE 754-2008) many many times. I've looked hard in the past for language that would give this exact connection, about < signalling. It just isn't there in either document, and it's dishonest to claim it is.
msg136476 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2011年05月21日 20:06
> and it's dishonest to claim it is.
This language was going too far, and I apologise for it. I think I need one of those 'wait 5 minutes before allowing you to post' controls.
msg136478 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011年05月21日 20:26
On Sat, May 21, 2011 at 3:32 PM, Mark Dickinson <report@bugs.python.org> wrote:
..
> That might be viable (a math module function might also make sense here), though it feels a bit YAGNI to me.
I have to admit that it would be YAGNI for most of my code because it
uses numpy for numeric calculations. Still, for consistency with
decimal, it may be a good addition.
Going a bit off-topic, I would like to mention the feature that may
actually be quite useful: float.sorting_key() that will return an
integer for each float in such a way that keys are ordered in IEEE 754
total ordering. Note that decimal has compare_total() that can be
used for sorting, but a cmp-style method is less useful than a key
since in py3k sort does not take cmp function anymore. Nice thing
about IEEE 754 is that float.sorting_key() can be implemented very
efficiently because one can simply use float's binary representation
interpreted as an integer for the key.
> If we were going to add such a method, it should follow IEEE 754: nan.max(x) == x.max(n) == x,
> but also nan.min(x) == x.min(nan) == x, for finite x. (See section 5.3.1.)
Agree. Unfortunately, numpy does not do it that way:
nan
>>> maximum(1.0, nan)
nan
I am not sure whether this is an argument for or against
float.max/min: if numpy had properly defined maximum, I would just
recommend to use that.
msg358837 - (view) Author: Marco Sulla (Marco Sulla) * Date: 2019年12月23日 21:35
marco@buzz:~$ python3.9
Python 3.9.0a0 (heads/master-dirty:d8ca2354ed, Oct 30 2019, 20:25:01) 
[GCC 9.2.1 20190909] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from decimal import Decimal as Dec, BasicContext as Bctx
>>> a = Dec("1981", Bctx)
>>> b = Dec("nan", Bctx)
>>> a.max(b)
Decimal('1981')
>>> b.max(a)
Decimal('1981')
>>> Bctx.max(a, b)
Decimal('1981')
>>> Bctx.max(b, a)
Decimal('1981')
`Decimal` completely adheres to IEEE 754 standard.
There's a very, very simple and generic solution for builtin min and max:
_sentinel = object()
def max(*args, key=None, default=_sentinel):
 args_len = len(args)
 
 if args_len == 0:
 if default is _sentinel:
 fname = max.__name__
 raise ValueError(f"{fname}() expected 1 argument, got 0")
 
 return default
 elif args_len == 1:
 seq = args[0]
 else:
 seq = args
 
 it = iter(seq)
 
 vmax = next(it, _sentinel)
 
 if vmax is _sentinel:
 if default is _sentinel:
 fname = max.__name__
 raise ValueError(f"{fname}() arg is an empty sequence")
 
 return default
 
 
 first_comparable = False
 
 if key is None:
 for val in it:
 if vmax < val:
 vmax = val
 first_comparable = True
 elif not first_comparable and not val < vmax :
 # equal, or not comparable object, like NaN
 vmax = val
 else:
 fmax = key(vmax)
 
 for val in it:
 fval = key(val)
 
 if fmax < fval :
 fmax = fval
 vmax = val
 first_comparable = True
 elif not first_comparable and not fval < fmax:
 fmax = fval
 vmax = val
 
 return vmax
This function continues to give undefined behavior with sets... but who calculates the "maximum" or "minimum" of sets?
History
Date User Action Args
2022年04月11日 14:57:16adminsetgithub: 56195
2019年12月23日 21:35:06Marco Sullasetnosy: + Marco Sulla
messages: + msg358837
2011年05月21日 20:26:53belopolskysetmessages: + msg136478
2011年05月21日 20:06:35mark.dickinsonsetkeywords: + gsoc

messages: + msg136476
2011年05月21日 19:54:46mark.dickinsonsetmessages: + msg136473
2011年05月21日 19:44:28belopolskysetmessages: + msg136470
2011年05月21日 19:32:27mark.dickinsonsetmessages: + msg136468
2011年05月21日 19:22:15mark.dickinsonsetmessages: + msg136467
2011年05月17日 02:13:53rhettingersetassignee: rhettinger
2011年05月16日 18:59:48belopolskysetmessages: + msg136121
2011年05月03日 19:21:13rhettingersetmessages: + msg135062
2011年05月03日 18:46:41belopolskysetmessages: + msg135058
2011年05月03日 18:41:02rhettingersetstatus: open -> closed

nosy: + rhettinger
messages: + msg135057

resolution: not a bug
2011年05月03日 18:34:21pitrousetnosy: + pitrou
messages: + msg135056
2011年05月03日 18:31:28belopolskycreate

AltStyle によって変換されたページ (->オリジナル) /