2

I have an array with 7 elements and I'm trying to get a random number between 0 - 6 so I can select an element in the array at random.

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

class Color{

public:

    Color(){

        colors[0] = "red";
        colors[1] = "orange";
        colors[2] = "yellow";
        colors[3] = "green";
        colors[4] = "blue";
        colors[5] = "indigo";
        colors[6] = "violet";

    }

    void printColors()
    {
        for (int i = 0; i<sizeof(colors)/sizeof(colors[0]); ++i)
        {
            cout << colors[i] << endl;

        }
    }

    void printRandomColor()
    {

        int random_integer = rand() % 7;
        cout << random_integer << endl;

    }


private:

    string colors[7];

};

int main(int argc, const char * argv[]) {

    srand( static_cast<unsigned int>(time(0)));

    Color colorObject;

    colorObject.printRandomColor();

    return 0;
}

When I do rand() % 7 I keep getting 6, but if I do rand() % 6 I end up getting random numbers. What gives?

I call srand( static_cast<unsigned int>(time(0))); in my main()

Brosef
  • 2,945
  • 6
  • 34
  • 69
  • 2
    This should work ok. There is something else going on in your code which is missing from the example. – Serge Jun 25 '17 at 00:44
  • 1
    @Serge I included the full code. Doesn't look to me like anything else in the code should affect the value of `random_integer` in the `printRandomColor` function. – Brosef Jun 25 '17 at 00:51
  • Not sure what is going on in your case. I just compiled your code and it worked ok. Make sure that you really compiled it and that you run a correct executable. Check date of your executable. You can also add prints for 'time(0)' to make sure it works correctly. – Serge Jun 25 '17 at 01:29
  • @Serge: It's very bizarre, I can reproduce with clang on macOS. – l'L'l Jun 25 '17 at 01:30
  • works ok with gcc. – Serge Jun 25 '17 at 01:33
  • Whatever you describe looks like a clang library bug. Someone who has clang experience might be be able to answer it. – Serge Jun 25 '17 at 01:36
  • @Serge: I definitely agree — it shouldn't exhibit this type of behavior. The randomness can be predicted also based on the timing, so I think there might be some type of bug perhaps. – l'L'l Jun 25 '17 at 01:38
  • 1
    Good opportunity to learn how to use `` ;) – Galik Jun 25 '17 at 01:41
  • 1
    @Brosef: I reported this bug to Apple, so we'll see what they say... – l'L'l Jun 25 '17 at 02:05
  • @l'L'l please let us know when you hear back – Brosef Jun 25 '17 at 02:38
  • [This](https://opensource.apple.com/source/Libc/Libc-1158.50.2/stdlib/FreeBSD/rand.c.auto.html) is, I think, the latest revision of the C library. I haven't investigated it yet. – Brett Hale Jun 25 '17 at 06:52
  • If you have time and desire to experiment with your own implementation of rand, here is a pointer to the glibc implementation of the function. it is actually simple enough: https://stackoverflow.com/questions/18634079/glibc-rand-function-implementation. – Serge Jun 25 '17 at 11:32
  • @Brosef: Finally heard back from Apple, see the edit in my answer. – l'L'l Jul 27 '17 at 21:07

3 Answers3

0

I noticed the same behavior with the code shown in the question:

rand() % 7    // always shows 6
rand() % 14   // always shows 6 or 13
rand() % 21   // always shows 6, 13, or 20

The problem is peculiar and there seems to be a pattern involved. Based on the comments that some aren't able to reproduce it, I decided to compile the code, with gcc on a Linux based machine and clang on macOS; Linux seems to behave normally from what I can tell, however macOS does not. I even tried completely different code just make sure it wasn't something else, yet got the same result.

#include <cstdlib>
#include <iostream>
#include <ctime>

int main() 
{
    int min = 1;
    int max = 7;

    std::srand(std::time(0)); // use current time as seed for random generator
    // int random_variable = std::rand() % max; // always returns 6
    // int random_variable = std::rand() % (max - min) + min; // produces 'predictable' numbers based on the time.
    int random_variable = RAND_MAX % std::rand() % (max-min) + min; // also returns predicate results based on the timing, except in reverse.

    std::cout << "Random value on [0 " << RAND_MAX << "]: " 
              << random_variable << '\n';
}

The only way I was able to get seemingly random results from rand() was to do:

RAND_MAX % std::rand() % (max-min) + min; // predictable based on timing

The issue is odd, and might be a bug with Clang; I'm at a loss at to what exactly is at play here. I would probably recommend using something other than rand() such as the <random> library mentioned in the comments perhaps.

EDIT: After reporting this bug to Apple this was the response:

Apple Developer Relations July 27 2017, 11:27 AM

There are no plans to address this based on the following:

std::rand directly uses rand from the C library. rand is known and documented to be broken (and is not going to change since people depend on its specific behavior).

From the man page: RAND(3) BSD Library Functions Manual

NAME rand, rand_r, srand, sranddev -- bad random number generator

DESCRIPTION These interfaces are obsoleted by arc4random(3).

For good pseudorandom numbers in C++, look at from C++11. E.g.: http://en.cppreference.com/w/cpp/numeric/random

Based on this information RAND() is broken and won't be fixed — use an alternative random number generator.

l'L'l
  • 44,951
  • 10
  • 95
  • 146
  • 1
    Does `RAND_MAX % std::rand() % (max-min) + min;` work for you? – Brosef Jun 25 '17 at 01:39
  • @Brosef: No, I found that it's possible to predict the next number based on the timing. It's simply using the time to go sequentially 6 5 4 3 2 1... – l'L'l Jun 25 '17 at 01:47
  • If you do `rand()%7`, then you are finding the remainder modulo 7. If you do `rand()%(max-min)+min` with `max=7, min=1`, then you are finding the remainder modulo 6 and adding 1. The first one produces seven distinct values from 0 to 6; the second one produces six distinct values from 1 to 6. So if the "random" number generator always produces numbers which are 1 modulo 7, that will be more apparent with the first call. And it's pretty well-known that the low order bits of the first call to an LCG are highly correlated to the seed... – rici Jun 25 '17 at 05:21
  • ... it's also well-known that you shouldn't use just the low-order bits of an LCG. Better is `(uint64_t)(rand())*(max-min)/(uint64_t)(RAND_MAX) + min`, which uses the high-order bits of the generated number. Also, despite the fact that "everyone does it", seeding with the time in seconds sucks. If you have a `/dev/urandom` (or `/dev/random`), read `sizeof int` bytes from it to seed your PRNG. – rici Jun 25 '17 at 05:24
  • @rici: I definitely agree with everything you've said, and the example above really is not indicative of best practices at all of course. Given that, the example should return something other than `6` each and every time using `std::rand() % 7` though right? And yes, seeding from time does suck - especially when the `rand()` function is simply a loop (in this case) that goes through the numbers sequentially instead of randomly for whatever unknown reason. – l'L'l Jun 25 '17 at 05:31
  • Yes, always returning a number which is 6 modulo 7 is not appropriate behaviour even for a non-optimal random number generator. But I don't believe that it "goes through numbers sequentially"; if it did that, it would generate sequential values after %7 as well. It's more likely sequencing by a multiple of 7. – rici Jun 25 '17 at 05:47
  • @rici: Here's the output from `std::rand() % 7` and `std::rand() % (max-min) + min` using a `100000 ms sleep`: https://gist.github.com/anonymous/e621f1e5e9877d445df0581232962cfa. – l'L'l Jun 25 '17 at 06:22
  • Rand is deterministic, so if you call srand with some value, you can later call it with the same value and you'll get the same random number sequence. So that output is only surprising for the fact that it always produces the same value mod 7. We know that there is a correlation between the seed and the first number generated after seeding. So the only surprise in your output is the fact that rand()%7 is always 6. It would be helpful to see the seed and the actual value returned by rand() after seeding. The actual number, not the number mod 6 or mod 7. – rici Jun 25 '17 at 06:38
  • @rici: Apple responded to my bug report and they said `rand()` is definitely broken and won't be fixed; the solution is to use an alternative random number generator. Cheers! – l'L'l Jul 27 '17 at 21:10
0

rand() is terrible. rand() % range is worse. Don't use it. Use arc4random_uniform().

#include <iostream>
#include <cstdlib> // Needed for arc4random_uniform()

int main(int argc, char *argv[]) {
    // Random number between 0 and 6.
    std::cout << arc4random_uniform(7) << std::endl;
}

So in your case:

void printRandomColor()
{
    int random_integer = arc4random_uniform(7);
    cout << random_integer << endl;
}

If portability is desired, then here is a C++ standard example. To me, it's needlessly more complicated and runs slower, but hey… it's the C++ standard.

#include <iostream>
#include <random> // For std::random_device and std::uniform_int_distribution

int main() {
    std::random_device randomizer;
    std::uniform_int_distribution<int> distribution(0, 6);

    // Random number between 0 and 6.
    int random_integer = distribution(randomizer);
    std::cout << random_integer << std::endl;
}
Jeffery Thomas
  • 42,202
  • 8
  • 92
  • 117
  • why relying on such function when you can simply use standard features? – The Techel Jun 25 '17 at 10:08
  • @TheTechel On BSD derived systems, `arc4random()` is available and relatively quick; however, if portability is desired, then `rand()` is still a bad choice. I added a C++ standard example. – Jeffery Thomas Jun 25 '17 at 11:38
-5

I would like to point out, that you are using a Random (Rand) operator, then trying to find out if the result has a Remainder (%), the Result will be the Remainder, which is where your strange math comes from. This is known as the Modulo Operator or Modulus Operator if you desire to Google it, although you should know that it actually has a slightly different name in C#, there is a Post in StackTrace about it Here:

What does the '%' operator mean?

If you open the Calc.exe Windows Program it is listed in Scientific Mode (Alt+2) as Mod.

Specifically, the way % operates is ((x - (x / y)) * y)

The above URL is a direct link to my answer where I point out specifically HOW it differs from standard / complete with a long drawn out example simulating all of the math step by step, the result returns a 0 for % and a 1 for / since the / Operand does roundUp() whilst % does roundDown() from what I've understood in the other Answers in that Post.

Update

I would at least like to have this answer here to provide reference for the Modulo Operator which is mentioned in the title of this question.

I didn't post this specifically as an answer per se, but more as reference material to avoid spam posts in the future.

If this is in fact a discovered bug, then this question is going to be picked apart letter by letter, symbol by symbol, and it's going to assist everybody involved to have this reference material here.

If I didn't know already it was named Modulo/Modulus in most languages, I would wonder what he meant by "Modulo" as he never explains anywhere that the % is named exactly that.

This answer addresses the fact that % uses roundDown() whereas / uses roundUp() complete with a referenced compile-able example written painstakingly in expanded step-by-step longhand which I then converted to C#.

I also would like to reiterate, as I mentioned in the comments, I have zero knowledge about xCode, I am somewhat familiar with C# and have provided this information in the C# context which this question is tagged with.

halfer
  • 19,824
  • 17
  • 99
  • 186
Blue64
  • 1
  • 3
  • 2
    this is off topic, it has nothing to do with the behavior of 'rand()'. The latter is supposed to provide normally distributed set of values. Any arithmetic operation on the result of it should provide a random result. '%' is such an operation. – Serge Jun 25 '17 at 02:54
  • It's not off topic as it is in fact literally a portion of the problem at hand. everybody is looking at `rand()` without troubleshooting if Modulo/Modulus (`%`) is a potential cause. Whilst you might recognize the flaw, people who come across this topic who don't know what `%` actually does would have zero reference material to confirm themselves what the bug is. I don't mind a 0 Score, but don't down vote my answer as irrelevant since it is relevant. – Blue64 Jun 25 '17 at 03:06
  • I would also like to point out that the Operand `%` is nearly impossible to Google unless you know it's name, I only found the referenced Post as a Link in another StackOverflow Post asking specifically what `%` Operand was in reference to C# (which this post is specifically about, as is the Answer I Linked) because they were having trouble locating it on Google since Google doesn't correctly parse `%` in any ways that I'm aware of (including `"%"` failing). Also, who knows, maybe one of the `using` is all he needs, I would like to hear from the Author before this gets blasted to bits. – Blue64 Jun 25 '17 at 03:15
  • and, before you ask, no, I am not familiar with xCode in the slightest. – Blue64 Jun 25 '17 at 03:19
  • I am not happy about requiring Meta in my Answer when a simple Comment could have cleared it up without the need for attempting to Down Vote to Deletion. – Blue64 Jun 25 '17 at 05:22
  • 2
    Please don't add excessive meta-commentary to posts. I was minded to roll back that whole edit, since it is largely a complaint that new readers are not likely to find useful. I have however moved it to the end and trimmed it lightly - the disagreement about voting should definitely not be added to posts. FWIW if I get a heavily downvoted answer, I just accept that people did not like it, and will either delete it or let them vote. – halfer Aug 19 '17 at 16:24