Hot to get a number in positive or negative range?

Started by
7 comments, last by erissian 16 years, 10 months ago
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.
Advertisement
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...
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.
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?
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
Ring3 Circus - Diary of a programmer, journal of a hacker.
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.
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
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.
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.
We''re sorry, but you don''t have the clearance to read this post. Please exit your browser at this time. (Code 23)

This topic is closed to new replies.

Advertisement