C++ and rand()
February 17, 2009 10:55 AM   Subscribe

C++ and the rand() function. Please explain why this piece of code works the way it does: cout << rand() % 20 << endl;

srand(time(0));
cout << rand() % 20 << endl;


This returns a random number between 0 and 20, as promised by the book I'm reading as well as internet tutorials.

If I remove "% 20" from the code and am left with

srand(time(0));
cout << rand() << endl;


This returns a really big number like 1847292034.

Looking at the code, I would think that adding "% 20" should do nothing more than divide a random number like 1847292034 by 20 and return the integer portion (92364601). But instead the compiler parses it in a different way that says not to generate a random number larger than 20.

Am I interpreting this wrong? Why does the compiler interpret this as something other than a mathematical operation? Is this type of thing common in C++?

Thanks!

(I'm using g++ to compile on a Ubuntu system.)
posted by Ziggy Zaga to Computers & Internet (13 answers total)
 
Best answer: Yes, you're reading it wrong.

% is the modulus operator. It does not return the integer portion of the division operation, but the "remainder".

17/20 = 0, remainder 17
17%20 = 17

23/20 = 1, remainder 3
23%20 = 3
posted by phrakture at 10:57 AM on February 17, 2009


Best answer: Remember when you were in third grade and learning long division and you'd end up with your answer in the form of X remainder Y? X was the integer division and Y was what was left over (i.e., the remainder)? Well, the % operator gives you that remainder.

Any integer divided by 20 is going to have a remainder between 0 and 19. That's why you get the answer you do.
posted by rbs at 10:59 AM on February 17, 2009


n modulo m = {0, 1, ..., m - 1}
posted by Blazecock Pileon at 11:00 AM on February 17, 2009


Agree with the others.

Also, however, the premise of your question is subtly wrong. There is no 'integer portion' as a result of any operation involving an integer value. The result is simply an integer, and any fractional part of the result will be truncated, not rounded.

So, if you replaced the modulo operator in that statement with a division operator, for example, you'd still only see an integer value being output. To get around this you'd have to do something like the following:


cout <>


Note the use of parentheses to guarantee precedence; the use of the cast '(float)' to convert the result of the call to 'rand' to a floating point number; and the use of the 'F' after the 20 to tell the compiler that it is a floating point number.

Apologies if this is a bit over your head as a beginner, but mixing up integers and floats can cause horrible to track down bugs in code, so it's good to get your head around it from the start.

Good luck with the learning. Once you get past the feeling that nothing makes sense, C++ can be fun.
posted by veedubya at 11:27 AM on February 17, 2009


Ach!

Preview tricked me into believing that would come out ok. Trying again:


cout << (((float) rand()) / 20F) << endl;

posted by veedubya at 11:32 AM on February 17, 2009


Others are right: x%y is a value on the interval [0,y).

On some computers, "rand() % max" will give a low-entropy sequence like 20,20,20,4,20,20,20,.... The rand() man page reprints a comment and fix from Numerical Recipes in its "notes" section.

Ubuntu's rand() doesn't have this disease, but if you keep programming you may encounter it.
posted by fantabulous timewaster at 11:36 AM on February 17, 2009


Also your book is wrong or at least misleading. rand() % 20 won't return a number between 0 and 20. It will return a number between 0 and 19 inclusive. You should never get 20. Additionally, as I see this come up astonishingly often on a programming forum I'm on, you generally want to call srand ONCE per program (there are exceptions of course). As such it's usually best to call it first thing in main before doing anything else.

Lastly, if you want the behavior you described above you would just use integer division. This is useful in some situations I've found, just not the ones you described.

int x = 1847292034 / 20; // x will equal 92364601
posted by Green With You at 12:06 PM on February 17, 2009


n modulo m = {0, 1, ..., m - 1}

x%y is a value on the interval [0,y)

Careful. While this is true in mathematics, and in the minds of all right-thinking people, it is not guaranteed in C or C++ (or many, many other languages and programs) when negative numbers are involved. Last I checked, the behavior in C and C++ isn't even well-defined.
posted by madmethods at 12:11 PM on February 17, 2009


madmethods sez:
Last I checked, the behavior in C and C++ isn't even well-defined.


It's defined, although one can debate about the "well" part.

K & R 2, a.k.a. "The C Programming Language, 2nd Edition", says (A7.6, page 205):

"The binary / operator yields the quotient, and the % operator the remainder, of the division of the first operand by the second; if the second operand is 0, the result is undefined. Otherwise, it is always true that (a/b)*b + a%b is equal to a. If both operands are non-negative, then the remainder is non-negative and smaller than the divisor; if not, it is guaranteed only that the absolute value of the remainder is smaller than the absolute value of the divisor."

The C ANSI standard says basically the same thing, but omits the last sentence dealing with negative operands. I don't have the C++ standard handy, but it's likely to be the same as one of these two.
posted by doorsnake at 12:53 PM on February 17, 2009


Response by poster: Thanks for the responses.

Lastly, if you want the behavior you described above you would just use integer division.
posted by Green With You at 3:06 PM on February 17


That really isn't the behavior I wanted. Your comment did make me realize that I confused the division and modulus operators which is why I couldn't understand what was going on with that code.

Thanks again!
posted by Ziggy Zaga at 1:37 PM on February 17, 2009


Since your main question was answered:

Am I interpreting this wrong? Why does the compiler interpret this as something other than a mathematical operation? Is this type of thing common in C++?

Unfortunately, while "%" was used in a straightforward way in this case, the answer to your last question in general is "yes."

C++ has "operator overloading", which frequently means that, yes, that innocent looking "+" or "/" or "%" or whatever could, in fact be doing something funky and/or slow. As an example, the "<<" operator originally, in C, meant "bit shift" (roughly speaking, multiply the left side by two to the power of the right side.) If you type "1 << 3", you'll get 8. But, through the miracle of operator overloading, in the context of the "cout" object, it means "output." Everyone has a different idea on which operators to abuse for what purpose (for example, one vector match library may use "*" for dot product, while another uses "^", etc.) What a given line of code is doing is not always obvious without a lot of additional context (of course, that's pretty much the goal of OO, the idea being that the language has somehow "saved" you from having to understand what the line of code you are looking at is doing.)

So, lots of fun there. A lot of (dare I say almost all?) modern OO languages deliberately do not include operator overloading.
posted by blenderfish at 2:43 PM on February 17, 2009


blenderfish: The advantage of operator overloading is you can write generic functions that work on built-ins and user-defined types alike. I agree that this language feature can easily be misused, the same way that the only thing stopping you from writing a function called "save" that deletes all your data is your own good sense. Probably overloading attracts a little more misuse because of it's whiz-bang-ness and the fact that it can save lazy programmers precious keystrokes (for me, about 10% of my development time is spent typing, a drop in the bucket compared to the amount of time spent, say, fucking around on Metafilter) but I still think it's reasonable to blame the programmers for the misuse of everything but the comma and address-of operators, which: don't ever use.

Also, I think you'll find that more "modern languages" allow operator overloading than don't.
posted by aubilenon at 3:29 PM on February 17, 2009


The advantage of operator overloading is you can write generic functions that work on built-ins and user-defined types alike

This can be done via other mechanisms (c.f. STL algorithms.)

I agree that this language feature can easily be misused, the same way that the only thing stopping you from writing a function called "save" that deletes all your data is your own good sense.

The standard counterargument to any negative opinion of OO is "well, that's not OO's fault, that's someone using it wrong." Which seems counter to a major stated intent of OO, which is to make things safer (more encapsulated, etc.) It's a major ongoing religious debate, and probably not appropriate to completely hash out here. The problem in this case is that it makes code non-explicit, that is difficult to understand without context, with no obvious 'pointer' to that context (like a function call.) In many cases it is a short-term win (like you say, it saves typing,) and in some of those cases, where everyone has standardized on a usage (like iterators in STL, or bit shift for cout, or maybe some vector math library that everyone on your project is well-versed in) it isn't as much of a 'gotcha,' but man, things can get real ugly real quick with operator overloading.

Also, I think you'll find that more "modern languages" allow operator overloading than don't.

I guess I was pretty much fixating on C# and Java. Looks like they've added it to C#, so I stand corrected.

Anyway, not saying it's 100% bad, but definitely someone aspiring to be a C++ programmer should know about it, because it can make things look straightforward that are actually complex (which, of course, is the best and worst part of OO.)
posted by blenderfish at 4:13 PM on February 17, 2009


« Older How can I convince the Apple Store to exchange my...   |   What could a sudden strange chemical smell be? Newer »
This thread is closed to new comments.