To generate a number that's in [st, ed) even after rounding, you can do random.uniform(st, closest_smaller_than_ed)
.
To get the closest number to ed which is smaller than ed use (see this):
numpy.nextafter(ed, ed - 1)
You may want to check that ed != st
to avoid generating a number not in [st, ed)
.
Similarly, to get val in (st, ed] do:
if st == ed:
return ed
st_adjusted = numpy.nextafter(st, st + 1)
return random.uniform(st_adjusted, ed)
EDIT:
The comment by dimo414 is correct, there are values of ed that for them, the probability of getting closest_smaller_than_ed will be zero. Proof:
(I'll call closest_smaller_than_ed by the name adj_ed)
The method random.uniform(st, adj_ed)
equals to st + (adj_ed - st) * random.rand()
. The maximum here is reached when random.rand()
gives the closest number that's smaller than 1, which is numpy.nextafter(1, 0)
.
So the question is, is there an ed value such that st + (adj_ed - st) * numpy.nextafter(1, 0) < adj_ed
. If there is such an ed, then for this ed there is no chance of getting adj_ed using my method, since the largest number we can get is smaller than adj_ed.
The answer is yes, there is such an ed: st = 0; ed = 360
, as the OP suggested.
This gives:
>>> st = 0
>>> ed = 360
>>> adj_ed = numpy.nextafter(ed, ed - 1)
>>> adj_1 = numpy.nextafter(1, 0)
>>> st + (adj_ed - st) * adj_1
359.99999999999989
>>> adj_ed
359.99999999999994
>>> st + (adj_ed - st) * adj_1 == numpy.nextafter(adj_ed, adj_ed - 1)
True
EDIT2:
I have a new idea, but I'm not sure it solves everything.
What if we first check that the biggest value that random.uniform(st, ed)
can give is ed (if it isn't then everything is OK and there is nothing to solve). And only then we use random.uniform(st, closest_smaller_than_ed)
as I suggested before.
def does_uniform_include_edge(st, ed):
adj_1 = numpy.nextafter(1, 0)
return st + (ed - st) * adj_1 == ed
def uniform_without_edge(st, ed):
if does_uniform_include_edge(st, ed):
adj_ed = numpy.nextafter(ed, ed - 1)
# if there is an ed such that does_uniform_include_edge(st, adj_ed) is False then this solution is wrong. Is there?
return random.uniform(st, adj_ed)
return random.uniform(st, ed)
print(uniform_without_edge(st, ed))
For st = 0; ed = 360
we have does_uniform_include_edge(st, ed) == False
so we just return random.uniform(st, ed)
.