Sign in to follow this  
Scythen

Hot to get a number in positive or negative range?

Recommended Posts

Hi all, Right now I have a situation where I need random numbers between a min and max value. Simple enough, I just lerp between then using a random s value of [0, 1]. A situation has come up where I need numbers in the two ranges [-max, -min] and [min, max]. If min were 0 then I could just do (2 * value – max) but min is not zero. Is there some way to go from the range [min, max] to the ranges [-max, -min] - [min, max]? Oh, and I don’t want to use any branches (if, else) just a straight mathematical solution if possible.

Share this post


Link to post
Share on other sites
Not so fast, thats just a lerp

What I want is something like a lerp where
s = [0, .5] t = [-max, -min]
s = [.5, 1] t = [min, max]

That’s the idea anyway...

[edit] Not sure where the post I responded to went... oh well...

Share this post


Link to post
Share on other sites
for S in [0,1]

do:
unsigned int S2 = S * 2.0;

float((S2+1)%2) * RangeScale(-max,-min,S) +
float((S2+0)%2) * RangeScale( min, max,S)

where
RangeScale ( float min, float max, float T )
{
return (T*(max-min) + min);
}

since the % will always be 0 for one of them, and 1 for the other.
if you want a different split from 50/50, you'd skew your calculation
of S2. ie. (S*S)*2.0 or maybe (1-S*S)*2.0, anything that gives S a non-uniform distribution over [0,1]


Your only issue with this code would be the casts from int to float
may or may not be faster than an if statement.
So profile and find out.

Share this post


Link to post
Share on other sites
Hmmm, that almost works.

One problem is with the S passed to range scale.
The negative side will only be evaluated with S [0, .49] and the positive side with S [.5, .99]. So the result will be half the desired range for the negative side and half the range for the positive side.

I think this would work though.

float result = float((S2+1)%2) * lerp(-min, -max, 1-S*2) +
float((S2+0)%2) * lerp( min, max, S*2-1);

Anyone got a other ideas?

Share this post


Link to post
Share on other sites
Why not create a random value in the interval [min, max] then negate it half of the time?

And may I ask why you'd like to avoid conditionals? In most languages, a conditional jump is much faster than the arithmetical equivalent. Not that this kind of micro-optimisation save anybody any performance.

Admiral

Share this post


Link to post
Share on other sites
This particular micro-optimization is actually important. This is part of the update code for a particle system. In most cases it would be irrelevant to avoid branches or the use of a random number generator but here, where the code gets executed thousands if not hundreds of thousands of times per frame it makes a difference.

The initial implementation was something like this:
value = lerp(min, max, randf()); // rand returns [0.0, 1.0]
value *= randi(0, 1) ? 1 : -1; // rand returns [0, 1]

Weather or not the suggested approach is more efficient has yet to be determined, but the second call to rand is expensive when in the middle of an update loop such as this.

As for branch prediction, yes it is very good when you are always taking the same branch. Here however, the branch taken is always changing because of the random values so the prediction is likely to be wrong a lot of the time (VTune backs this up).

If I can find a way to get values in the two ranges [-max, -min], [min, max] using only one random float value and no branches that would be ideal if the required code is not slower than generating a second random number.

Share this post


Link to post
Share on other sites
You seem to be using Cg.
step() may be slightly more efficient than fmod(). I don't know. Benchmark it.

Another very simple solution involves the ternary operator:

solution = s > 0.5 ? lerp(min, max, s) : lerp(-max, -min, s);

I think that on most graphics cards both expressions are evaluated and last the correct one is simply selected. Not sure though.

cheers,
nils

Share this post


Link to post
Share on other sites
INRANGE(min,max)
width ← max - min
value ← RANDOM-FLOAT(-width,width);
sign ← value / FABS(value);
return value + min * sign;


Assuming that the FABS operation is available in hardware, you need 6 operations and a random number to get the value.

Quote:
Original post by n115
Another very simple solution involves the ternary operator:

solution = s > 0.5 ? lerp(min, max, s) : lerp(-max, -min, s);

I think that on most graphics cards both expressions are evaluated and last the correct one is simply selected. Not sure though.


Depends on the size of the conditional expressions. Conditionals may also be implementing using a flag which prevents execution.

Share this post


Link to post
Share on other sites
Well, I don't agree with micro-optimization either, but here's my take:

Random functions are a big pain in the butt, so you don't want to call two of them; that's worse than anything else. Try this:


float f = my_random_lerp_thing(min,max);
int *fp = (int*)&f;
*fp += ((*fp & 0x00000001)<<31)


This takes the lowest precision bit and uses it to determine the sign bit. This way, you get alternating numbers on either side.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this