I need a clean approach for range-checking floats in Python

Question:

I’m looking for a simple way of range checking floats in Python where the minimum and maximum bounds may be null.

The code in question is:

    tval = float(-b - discriminant) / float (2*a)
    if tval >= tmin and tval <= tmax:
        return tval 

    tval = float(-b + discriminant) / float (2*a)
    if tval >= tmin and tval <= tmax:
        return tval

    # Neither solution was within the acceptable range.
    return None

However, this completely fails to handle the case where tmin or tmax is None (which should be interpreted to mean that there is no minimum or maximum respectively).

So far the best I’ve been able to come up with is:

    tval = float(-b - discriminant) / float (2*a)
    if (tmin == None or tval >= tmin) and (tmax == None or tval <= tmax):
        return tval 

    tval = float(-b + discriminant) / float (2*a)
    if (tmin == None or tval >= tmin) and (tmax == None or tval <= tmax):
        return tval

    # Neither solution was within the acceptable range.
    return None

I keep thinking there has to be a better (cleaner, more readable) way to write that. Any ideas?

Asked By: Adam Luchjenbroers

||

Answers:

First, some setup: we’ll need a float infinity constant.

INF = float(1e3000)

or

INF = float('inf')  # Python 2.6+

First option can be considered portable for practical purposes; just use some really huge value which is guaranteed to be outside the range representable by your platform’s floating point type. Second option is "truly" portable, but requires Python 2.6 or newer.

Now, your condition can be written this way (edit: only if tmin and tmax cannot be zero!):

if (tmin or -INF) <= tval <= (tmax or +INF) :
    return tval

Edit

I made a crude error by missing that 0.0 is a legitimate value for tmin or tmax. Thanks to Roger Pate for noticing.

Answered By: atzz

Maybe I’d define a checker function first, for readability:

def inrange(x, min, max):
    return (min is None or min <= x) and (max is None or max >= x)

tval = float(-b - discriminant) / float (2*a)
if inrange(tval, tmin, tmax):
    return tval 

tval = float(-b + discriminant) / float (2*a)
if inrange(tval, tmin, tmax):
    return tval 

# Neither solution was within the acceptable range.
return None

There must be a module defining such an inrange method somewhere, I bet. But I did not find it (nor look for it, though). 🙂

Answered By: Johannes Charra

Using INF from atzz’s answer (in a way that won’t fail when 0.0 is used):

def coalesce(*values):
  for v in values:
    if v is not None:
      return v

if coalesce(tmin, -INF) <= tval <= coalesce(tmax, INF):
  return tval

However, what you have is clear enough for me:

if ((tmin is None or tmin <= tval) and
    (tmax is None or tval <= tmax)):
   return tval
Answered By: Roger Pate

Using INF from atzz’s answer

if tmin is None: tmin = -INF
if tmax is None: tmax = +INF

tval = float(-b - discriminant) / float (2*a)
if tmin <= tval <= tmax:
    return tval 

tval = float(-b + discriminant) / float (2*a)
if tmin <= tval <= tmax:
    return tval

# Neither solution was within the acceptable range.
return None
Answered By: John La Rooy
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.