Thread: How to generate a random number in the interval of -1 and 1?

  1. #1
    Registered User
    Join Date
    Oct 2019
    Posts
    43

    How to generate a random number in the interval of -1 and 1?

    If R is a random number, R has to be in the interval of -1 and 1, i.e, -1<R<1. Any idea?

  2. #2
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    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?

  3. #3
    Registered User
    Join Date
    Sep 2020
    Posts
    425
    If it is an integer -1 < x < 1 then it doesn't leave many options....

  4. #4
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Quote Originally Posted by hamster_nz View Post
    If it is an integer -1 < x < 1 then it doesn't leave many options....
    INCLUDING these two is, of course, very easy. Excluding them is easy too, but there is a trick.

    Of course, I'm waiting to see some code before showing it...
    Last edited by flp1969; 03-30-2021 at 05:28 PM.

  5. #5
    Registered User
    Join Date
    Oct 2019
    Posts
    43
    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;
    }

  6. #6
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    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;
    }

  7. #7
    Registered User
    Join Date
    Dec 2017
    Posts
    1,626
    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;
    }
    A little inaccuracy saves tons of explanation. - H.H. Munro

  8. #8
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Quote Originally Posted by john.c View Post
    Another possibility. Generates values from -0.999999999068677 to 0.999999999068677, inclusive.
    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.

  9. #9
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    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.

  10. #10
    Registered User
    Join Date
    Dec 2017
    Posts
    1,626
    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?
    A little inaccuracy saves tons of explanation. - H.H. Munro

  11. #11
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Also, double's only have a precision of 15-17 decimal digits, so showing them to 30 (or 50) digits is meaningless.
    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.

    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.

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

    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:
    Code:
    double rndx( void )
    { return ( 2.0 * rand() - RAND_MAX) / (RAND_MAX + 2.0); }
    No casting needed and no need to worry about overflows.

    BTW, what does "simetrical" mean?
    Sorry... "symmetrical". Anyway: what "artifically" means?
    Last edited by flp1969; 03-31-2021 at 02:19 PM.

  12. #12
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    In time: You are correct about the duplicates. There are none. My mistake.

  13. #13
    Registered User
    Join Date
    May 2012
    Posts
    505
    Quote Originally Posted by Shafiul View Post
    If R is a random number, R has to be in the interval of -1 and 1, i.e, -1<R<1. Any idea?
    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
    Code:
    x = lerp(-1.0, 1.0, uniform());
    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.
    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


  14. #14
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    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 );
    }

  15. #15
    Registered User
    Join Date
    Sep 2020
    Posts
    425
    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);

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 2
    Last Post: 10-01-2016, 12:54 AM
  2. random number from interval [0,1]
    By nerio in forum C Programming
    Replies: 11
    Last Post: 02-07-2013, 07:24 AM
  3. Replies: 3
    Last Post: 07-24-2012, 09:30 AM
  4. Generate Random Number
    By peacealida in forum C++ Programming
    Replies: 10
    Last Post: 04-06-2008, 08:57 AM
  5. generate a random number
    By waxydock in forum C++ Programming
    Replies: 5
    Last Post: 06-05-2005, 07:43 PM

Tags for this Thread