-2

I'm trying to make it so that rand_draw holds a random positive number between 0-8. But this code keeps giving negative numbers on some iterations. What's happening?

srand(time(0));
int draw_count = 8;
int rand_draw = (2 * rand()) % draw_count;
cout << rand_draw << endl;
  • 5
    Why `2 * rand()`? That's likely giving you overflow, hence the negative numbers. – Fred Larson Feb 26 '21 at 15:52
  • 3
    `rand()` returns a value between 0 and `RAND_MAX`, which is commonly the same as `INT_MAX`. So half the time `2 * rand()` will overflow. This is undefined behavior but often the result is wraparound to a negative value. – Nate Eldredge Feb 26 '21 at 15:53
  • So how would I properly get rand_draw to give me a random positive number between 0 and 8 without overflow? – J. O'connor Feb 26 '21 at 15:55
  • 2
    And if it doesn't give you an overflow, you never get odd numbers. Anyway, don't use rand() in C++ unless there is absolutely no other way. Use `std::uniform_int_distribution` – Kit. Feb 26 '21 at 15:55
  • 1
    Don't use [rand](https://en.cppreference.com/w/cpp/numeric/random/rand). Use [uniform_int_distribution](https://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution) or [uniform_real_distribution](https://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution) – Thomas Sablik Feb 26 '21 at 15:56
  • this is for an intro c++ class, rand() is the only function we've been taught thus far for randomizing. I am not allowed to use things we haven't been shown ie uniform_int_distribution – J. O'connor Feb 26 '21 at 15:58
  • 3
    That's not an introduction to C++ but history of ancient C++. I don't get why this would be taught today. You get taught writing legacy code. This function wouldn't pass any serious code review. – Thomas Sablik Feb 26 '21 at 16:00
  • to be fair, it's for an extra credit problem. the textbook makes very brief mention of rand() and I am trying to modify it to suit my purposes. – J. O'connor Feb 26 '21 at 16:01
  • 1
    `rand` generates an integer number. You can use the modulo operator to limit the range. But remember: _"There are no guarantees as to the quality of the random sequence produced. In the past, some implementations of rand() have had serious shortcomings in the randomness, distribution and period of the sequence produced (in one well-known example, the low-order bit simply alternated between 1 and 0 between calls). rand() is not recommended for serious random-number generation needs. It is recommended to use C++11's random number generation facilities to replace rand(). (since C++11) "_ – Thomas Sablik Feb 26 '21 at 16:04
  • 1
    You have most of it. I just question why you multiply by two. Talk it over with [your rubber duck](https://en.wikipedia.org/wiki/Rubber_duck_debugging). – user4581301 Feb 26 '21 at 16:06
  • You shouldn't use `rand` for 9 years. You should probably update your textbook. – Thomas Sablik Feb 26 '21 at 16:09
  • Here's a quick summary reason of the difference between a language-built-in randomization, and something like `rand()`: https://stackoverflow.com/questions/64898700/math-question-about-random-x-and-random-x-java/64900995#64900995 (note while the link is for Java, the same question arises in just about every lang). – Rogue Feb 26 '21 at 16:09
  • 2
    @ThomasSablik • Making new programmers today write yesterday's legacy code for tomorrow's future! I'm looking forward to the past with anticipation! – Eljay Feb 26 '21 at 17:40
  • @Eljay we'll be partying with Marty McFly in 1955 before you know it. – user4581301 Feb 26 '21 at 18:41

3 Answers3

1

According to cppreference, rand():

Returns a pseudo-random integral value between ​0​ and RAND_MAX (0 and RAND_MAX included).

The value of RAND_MAX is implementation defined, but may very well be the maximum value that can be represented in an int. By doubling the value you get from rand(), the result may overflow into a negative number. This is actually undefined behavior.

I see no reason to double the return value of rand(). You could correct this quite simply:

int rand_draw = rand() % (draw_count + 1);

This will give you random values between 0 and 8, as you specified.

But in modern C++, using the uniform_int_distribution from the C++ Standard Library is a much better way to go. Here's the example from the linked page, modified to show the range you specified:

#include <random>
#include <iostream>
 
int main()
{
    std::random_device rd;  //Will be used to obtain a seed for the random number engine
    std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
    std::uniform_int_distribution<> distrib(0, 8);
 
    for (int n=0; n<10; ++n)
        //Use `distrib` to transform the random unsigned int generated by gen into an int in [0, 8]
        std::cout << distrib(gen) << ' ';
    std::cout << '\n';
}
Fred Larson
  • 60,987
  • 18
  • 112
  • 174
1

rand() function will generates a random number in this range [0, RAND_MAX). RAND_MAX is a large number. More details are discussed in this link

When you use 2*rand(), basically you are shifting the generated number 1 bit to the left. If the generated number is large enough that its second bit from the left side is 1, then after shifting to the left you are generating a negative number.

Here is an example: Let's assume the generated number in hexadecimal is 0x70000011. The four most significant bits of this number are 0111, after shifting 1 bit to left, you can see the sign bit is changing. then you try to use the % operation which results in negative number.

  1. rand() % draw_count -> generates number [0, draw_count)
  2. (rand() % draw_count) + draw_count -> generates number [draw_count, 2* draw_count)
  3. rand() % (2draw_count) -> generates number [0, 2 draw_count)
Ashkanxy
  • 2,380
  • 2
  • 6
  • 17
-2

I figured out a solution, given that I can only use the rand() function. I simply added an if-statement afterwards to check if it's even, and if so keep the variable set to rand_draw, and if not, increment by 1.

int rand_draw = rand() % draw_count;
if (rand_draw % 2 == 0)
{
    rand_draw = rand_draw;
}
else
{
    rand_draw += 1;
}
  • 1
    @ThomasSablik well, as long as [this one](https://xkcd.com/221/) does... – Kit. Feb 26 '21 at 16:20
  • 3
    You want random, positive, AND even? That should be in the question. `int rand_draw = (rand() % 4) *2;` Because modulo is at applied before doubling overflow won't happen. Note that we had to halve the range of of the random numbers. – user4581301 Feb 26 '21 at 16:27