How do I clip a floating-point number to just belo

2019-06-19 01:34发布

问题:

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?

回答1:

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.]



回答2:

Python's sys provides an float_info struct with an epsilon attribute and is defined as

difference between 1 and the least value greater than 1 that is representable as a float

So I would suppose something like

def clip(num):
    if(num >= 1):
        return 1 - sys.float_info.epsilon
    return num

should do the trick. Although this is generally bad, and there are probably tons of reasons why you should never attempt this.

EDIT I just observed one such reason - implementation. While CPython does what you'd expect, my first go-to choice is IronPython, which doesn't (although it's a bug). Ye be warned!



回答3:

In most practical cases you don't need to be infinitesimally smaller, you can approximate it. So for your example I'd use 0.9999999 instead of 1.0.