On Sat, Jul 04, 2020 at 11:54:47AM -0700, Christopher Barker wrote:
> Hmm.
> 
> 
> Since NaN is neither greater than nor less that anything, it seems the only
> correct answer to any
> Min,max,clamp involving a NaN is NaN.

If you want NAN-poisoning behaviour it is easy for you to add it 
yourself as a wrapper function, without it costing any more than it 
would cost to have that behaviour baked into the function. You get to 
choose if you want to handle floats only, or Decimals, or both, what to 
do with signalling NANs, or whether to bother at all. If you know your 
bounds are not NANs, why test for them at all?

But if you bake special NAN poisoning behaviour in to the function, 
nobody can escape it. Everyone has to test for NANs, whether they need 
to or not, and worse, if they want non-poisoned behaviour, they have to 
test for NANs *twice*, once to over-ride the builtin behaviour, and then 
a second time when they call the function.

When we have a choice of behaviours and no absolutely clear right or 
wrong choice, we should choose the behaviour that inconveniences people 
the least.

The beauty of the implementation I give is that the behaviour with NANs 
follows automatically, without needing to test for them. NAN poisoning 
requires testing for NANs, and that is not so easy to get right:

    py> math.isnan(2**10000)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    OverflowError: int too large to convert to float

    py> math.isnan(Decimal('sNAN'))
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: cannot convert signaling NaN to float

You want everyone to pay the cost of testing for NANs, even if they 
don't want or need to. I say, let only those who need to test for NANs 
actually test for NANs.

Why is this a debate we need to have?

In practice, the most common use-case for clamp will be to call it 
multiple times against different values but with the same bounds:

    # Not likely to be this
    for value in values:
        lower = something_different_each_time()
        upper = something_different_each_time()
        do_something(clamp(value, lower, upper))

    # Most commonly this:
    for value in values:
        do_something(clamp(value, lower, upper))

If you care about the remote possibility of the bounds being NANs, and 
want to return NAN instead, hoist the test outside of the call:

    # TODO: Need to catch OverflowError, maybe ValueError
    # maybe even TypeError?
    if math.isnan(lower) or math.isnan(upper):
        for value in values:
            do_something(NAN)
    else:
        for value in values:
            do_something(clamp(value, lower, upper))

and you only pay the cost once. Don't make everyone pay it over and over 
and over again when the bounds are known to not be NANs.



-- 
Steven
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/7RK5TSYDI6N4OUJ4SOSKWEYPHAPQFIFA/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to