If R is a random number, R has to be in the interval of -1 and 1, i.e, -1<R<1. Any idea?
If R is a random number, R has to be in the interval of -1 and 1, i.e, -1<R<1. Any idea?
Do you know how to get a random value in an interval of 0 to 2?
And... are you sure you need to exclude -1 and 1?
If it is an integer -1 < x < 1 then it doesn't leave many options....
The problem of the following formula(value = 2.0 * (rnumber - 0.5)), it provides sometimes -1 or +1 once the rnumber is 0 or 1.
Code:#include<stdio.h> #include<iostream> int main() { float rnumber, value; for (int i = 0; i < 1000000; i++) { rnumber = (double)rand() / (double)((unsigned)RAND_MAX); value = 2.0 * (rnumber - 0.5); if (value == -1 || value == 1) printf("%7.4f ", rnumber); } return 0; }
My soluction: Reducing precision by 1 ULP.
Code:#include <stdlib.h> #include <math.h> #include <float.h> // Transform rand() return value to [-1.0 e 1.0] range. #define XFRM_RAND(x) ( 2.0 * (double)(x) / RAND_MAX - 1.0 ) // OBS: 'double' because 'float' has only 24 bits of precision. double rand_simetrical ( void ) { double f, ulp; int e; f = XFRM_RAND ( rand() ); // Calculate ULP. frexp ( f, &e ); ulp = ldexp ( DBL_EPSILON, e - 1 ); if ( f >= 0.0 ) ulp = -ulp; // Get rid of 1.0 and -1.0 by making all values 1 ULP less precise. f += ulp; return f; }
Another possibility. Generates values from -0.999999999068677 to 0.999999999068677, inclusive.
Code:#include <stdio.h> #include <stdlib.h> #include <time.h> double rndx() { return ((unsigned)rand() + 1) / (double)((unsigned)RAND_MAX + 2) * 2.0 - 1.0; } int main() { srand(time(NULL)); printf("Low: %.15f\n", ((unsigned)0 + 1) / (double)((unsigned)RAND_MAX + 2) * 2.0 - 1.0); printf("High: %.15f\n\n", ((unsigned)RAND_MAX + 1) / (double)((unsigned)RAND_MAX + 2) * 2.0 - 1.0); for (int i = 0; i < 10; ++i) printf("%.15f\n", rndx()); return 0; }
The best argument against democracy is a five minute conversation with the average voter. - Churchill
Interesting the use of casting to unsigned int to avoid overflows. I have only 1 consideration:
RAND_MAX not necessarily is INT_MAX. It could be UINT_MAX. But, of course, rand() returns an int... It is safe to think as RAND_MAX is INT_MAX.
Anyway... I think this approach is very good!
PS: Sorry... your approach doesn't cause problems with the distribution, AFAIK...
Last edited by flp1969; 03-31-2021 at 08:01 AM.
Hummm... sorry again, john.c... Taking another look, your approach use the range between -0.999999999068677425384521484375 and 0.999999999068677425384521484375, inclusive. Mine is wider, from -0.999999999999999777955395074968691915273666381835 9375 and 0.999999999999999777955395074968691915273666381835 9375, inclusive.
Notice 1/RAND_MAX gives us a step of 2⁻³¹ (for RAND_MAX as INT_MAX). Your approach has an "error" of, approx, 9.3*10⁻¹⁰, witch gives us a step of approx 2⁻³⁰. This means half of possible values returned by rand() have duplicates in that range.
Well... my approach has a potential for exactly 2 duplicates only. If I got DBL_EPSILON or -DBL_EPSILON as result of the equation (never happens because 2⁻³¹ is very far from that epsilon).
Last edited by flp1969; 03-31-2021 at 09:08 AM.
Since rand() returns an int, obviously RAND_MAX, "the maximum value returned by the function rand," cannot be UINT_MAX.
Also, double's only have a precision of 15-17 decimal digits, so showing them to 30 (or 50) digits is meaningless.
I don't understand what you mean by "duplicates". Different values from rand() obviously yield different values as a double. There are no duplicates.
As for you "wider range", that's an artifact of your method which has an artifically high step between rand() returns of 0 and 1 and between RAND_MAX-1 and RAND_MAX.
Your method is overcomplicated and simply not worth it.
BTW, what does "simetrical" mean?
The best argument against democracy is a five minute conversation with the average voter. - Churchill
That's a common mistake. The precision of a floating point type is measured in bits, not decimal digits. In the case of a `double` type, using IEEE 754 format, if you have all the 53 bits set (the implicit one and all the 52 fracional) and an expoent of 0 in the "scale factor", the exact value represented will be 1.999999999999999777955395074968691915273666381835 9375 with these 53 decimal digits.Also, double's only have a precision of 15-17 decimal digits, so showing them to 30 (or 50) digits is meaningless.
The 15-16 decimal digits equivalence is an approximation based on the transformation from binary to decimal, as in:
p = log10(2⁵³) = 53*log10(2)
It means that not all decimal values can be represented exactly in binary (take 0.1 as an example, which is 0b0.000111001100... ad infinitum), so if you want an approximation you can take only 15 or 16 decimal digits and round to nearest, to be sure.
Not all decimal values can be represented, exactly, in binary but ALL binary values has an exact correspondence in decimal.
Why "artifically"? ULP is a consistent concept when using floating point values. All I did was to add/subtract 1 ULP, the smallest possible value based on a calculated value to avoid reaching 1.0 or -1.0.As for you "wider range", that's an artifact of your method which has an artifically high step between rand() returns of 0 and 1 and between RAND_MAX-1 and RAND_MAX.
Your method is overcomplicated and simply not worth it.
1- Get a random value in [0,1] range, multiply by 2 and subtract 1 to get [-1,1];
2- Subtract (or add) 1 ULP
I think this is less complicated to understand why 2*(r+1)/(R+2)-1 works as well. By the way, the same equation could be written as:
No casting needed and no need to worry about overflows.Code:double rndx( void ) { return ( 2.0 * rand() - RAND_MAX) / (RAND_MAX + 2.0); }
Sorry... "symmetrical". Anyway: what "artifically" means?BTW, what does "simetrical" mean?
Last edited by flp1969; 03-31-2021 at 02:19 PM.
In time: You are correct about the duplicates. There are none. My mistake.
define two macros. One is uniform(), which create a random number on 0-1. The other is lerp, which interpolates between a and b by paramter t, on 0-1.
So
Usually uniform() is implemented to return a value on 1 to 1 - epsilon, so you will never actually get 1 returned. This might or might not be acceptable.Code:x = lerp(-1.0, 1.0, uniform());
I'm the author of MiniBasic: How to write a script interpreter and Basic Algorithms
Visit my website for lots of associated C programming resources.
https://github.com/MalcolmMcLean
That's interesting, Malcom! I completely forgot about linear interpolation!
Code:#include <stdlib.h> #include <float.h> #define lerp(min_, max_, norm) \ ( (min_)*(1.0 - (norm)) + (max_)*(norm) ) double symmetrical_rand ( void ) { double d; d = ( double ) rand() / RAND_MAX; return lerp ( -1.0 + DBL_EPSILON, 1.0 - DBL_EPSILON, d ); }
Me? I would most likely do what I did when I needed random numbers that were inside a sphere. Generate random numbers between -1 and 1 (inclusive), and if it s >= 1.0 or <= -1.0 then just loop and try again.... so
Code:do { x = (double)rand()/RAND_MAX*2.0-1.0; while(x <= -1.0 || x >= 1.0);