Search code examples
pythonnumpyfloating-pointclipping

How do I clip a floating-point number to just below a limit?


Functions like numpy.random.uniform() return floating point values between a two bounds, including the first bound but excluding the top one. That is, numpy.random.uniform(0,1) may yield 0 but will never result in 1.

I'm taking such numbers and processing them with a function that sometimes returns results outside of the range. I can use numpy.clip() to chop values outside of the range back to 0-1, but unfortunately that limit is inclusive of the top number.

How do I specify "the number infinitesimally smaller than 1" in python?


Solution

  • Well, if you're using numpy, you can simply use numpy.nextafter:

    >>> import numpy
    >>> numpy.nextafter(1, 0)
    0.99999999999999989
    

    Note that (at least for me):

    >>> import sys
    >>> 1-sys.float_info.epsilon
    0.9999999999999998
    >>> numpy.nextafter(1, 0) - (1-sys.float_info.epsilon)
    1.1102230246251565e-16
    >>> numpy.nextafter(1, 0) > (1-sys.float_info.epsilon)
    True
    

    Incidentally, to second @Robert Kern's point that sometimes random.uniform will include the upper bound for some inputs other than (0, 1):

    >>> import random, numpy
    >>> numpy.nextafter(0,1)
    4.9406564584124654e-324
    >>> random.uniform(0, numpy.nextafter(0,1))
    0.0
    >>> random.uniform(0, numpy.nextafter(0,1))
    0.0
    >>> random.uniform(0, numpy.nextafter(0,1))
    4.9406564584124654e-324
    

    [I share the general sense that there is probably a better way to approach this problem.]