7

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?

mattdm
  • 2,082
  • 25
  • 39
  • If math is to be believed, you can't. – Ignacio Vazquez-Abrams Feb 23 '12 at 02:14
  • 2
    in most systems there is a concept of epsilon, which is the smallest increment – Keith Nicholas Feb 23 '12 at 02:15
  • Uh-oh, I can sense a `0.99999... < 1` flame war brewing – wim Feb 23 '12 at 03:33
  • 1
    I don't think I'm in paradox (or flame-war) land with this. Or are you saying that `numpy.random.uniform(0,1)` will actually sometimes return a number equal to 1? If that's the case, then, okay, fine. I don't really care about the paradox, but I want my modified-then-clipped numbers to be guaranteed to be in the same range that the originals are. – mattdm Feb 23 '12 at 03:41
  • 3
    I think wim is referring to the idea that the real number 0.9999... (repeating) is actually exactly 1 in mathematics. Floating point numbers are not real numbers, so it's not especially relevant to your question. However, I will take this opportunity to note the cases when `uniform(x,y)` might (extremely rarely!) give you results equal to `y`. It shouldn't happen with `uniform(0,1)`, but in other cases, the floating point arithmetic used to rescale the underlying `[0,1)` random number to your bounds might sometimes give `y` exactly. – Robert Kern Feb 23 '12 at 12:19
  • Thanks @RobertKern. I noticed that the Python `random` docs say that; the numpy docs should probably be updated include that disclaimer as well. – mattdm Feb 23 '12 at 12:56
  • Person who downvoted — any suggestion as to why? – mattdm Sep 19 '18 at 14:26

3 Answers3

7

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

DSM
  • 342,061
  • 65
  • 592
  • 494
  • why isn't it the case that `numpy.nextafter(1, 0) == (1-sys.float_info.epsilon)`, is it because the density of floats changes depending on magnitude? – wim Feb 23 '12 at 04:46
  • @wim: yep. 1+epsilon/2 = 1, but 1-eps/2 = nextafter(1,0). – DSM Feb 23 '12 at 04:52
3

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!

Gleno
  • 16,621
  • 12
  • 64
  • 85
  • So... is there a not-generally-bad approach? – mattdm Feb 23 '12 at 03:36
  • 1
    It really depends on why you need your function to be in [0, 1). Do you want to ignore values >= 1? Do you want to include those values as 1 - eps? Who's taking those values, and why does that guy need them to be [0, 1)? You state that there exists a library function u that returns [0, 1). Then you f(u()) these numbers and return [0, inf), but want to return [0, 1) ...why? Presumably there's an h(f(u)), and maybe we should focus on the h. :) – Gleno Feb 23 '12 at 05:21
2

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.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622