On Sun, Jul 5, 2020 at 6:01 PM Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
On 6/07/20 3:55 am, Steven D'Aprano wrote:
> With that interpretation, a NAN passed as the lower or upper bounds can
> be seen as another way of saying "no lower bounds" (i.e. negative
> infinity) or "no upper bounds" (i.e. positive infinity), not "some
> unknown bounds".

Python already has a value for representing missing or unspecified
data, i.e. None. So we don't need to use NaN for that, and can
instead reserve it to mean "no correct answer".

+1

and we can use +inf and -inf for unlimited bounds as well. Yes, they are a bit of a pain to write in Python, but we could do:

def clamp(value, min=-math.inf, max=math.inf):
    ...

yes, that would make them optional, rather than required, and therefore provide a slight asymmetry between specifying min only or max only, but still be handy. to make it more consistent, but maybe more annoying in the common case, we could make them keyword only.

> I agree with you that `clamp(lower=x, value=NAN, upper= x)` should
> return x.

I don't think I agree with that, because it relies on assuming that
the lower and upper bounds can meaningfully be compared for exact
equality, which may not be true depending on the circumstances.

and then we'd need to check if they were equal as well.
 
> Treat a NAN bounds as *missing data*, which effectively means "there is
> no limit", i.e. as if you had passed the infinity of the appropriate
> sign for the bounds.

and really how often would one end up with NaN as a bound anyway? Often they will be hard-coded. I"m having a really hard time imagining when you'd end up with NaN for a bound that was NOT an error!

It would be far more likely for the value you want clamped to be NaN -- and then it sure as heck should return NaN.

As for the behavior of min() and max() when provided a NaN (and much of Python's handling of FP special values) -- I think that's a practicality-beats-purity issue. I have a really hard time thinking that anyone thinks that:

In [81]: min(1, math.nan)                                                      
Out[81]: 1

In [82]: min(math.nan, 1)                                                      
Out[82]: nan
 
is ideal behavior!

while I was writing this:

On Sun, Jul 5, 2020 at 6:38 PM Steven D'Aprano <steve@pearwood.info> wrote:
...  on this point at least the IEEE-754 standard is firm: if a
function will return the same result for every non-NAN argument, then it
must return the same result for NAN arguments too.

    clamp(value, x, x)

will always return x for every finite and infinite value, so it must
return x for NANs too.

Except that Python (maybe except for the math module) does not conform to IEEE-754 in many other places.

So we do have a practicality beats purity choice here.

-CHB


--
Christopher Barker, PhD

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython