Thread: Stuck on Logic for "10,000" Random Number generator in C++/CLI

1. No Profile Picture
Contributing User
Devshed Newbie (0 - 499 posts)

Join Date
Aug 2013
Posts
232
Rep Power
2

Stuck on Logic for "10,000" Random Number generator in C++/CLI

Hello everyone,

So as usual, I'm trying another exercise in my C++ book, and the thing is, I think I got most of the logic of what I really want to convey via comments, and maybe 50% of the code, but its the logic near the end that's making me stuck for some reason.

Basically the core requirements for the program are as follows:

Code:
```/*5. Write a C++/CLI program that will generate a random integer greater than 10,000.

Output the integer and then output the digits in the integer in words.

For example, if the integer generated were 345678, then the output should be:
The value is 345678 three four five six seven eight
*/```
The goal itself is pretty clear, generate a number that's greater than 10,000 using a random number generator like last time, but this time, somehow go through each digit and print out it's text form as well.

I thought maybe I could first declare the random number integer variable, use the random number generator and assign a value to that integer. Declare an integer array that will store each digit from random_number that's "n" numbers big because I don't know exactly what number the computer will generate.

Then, I was thinking to make a for loop that will assign each element of the integer array to each digit of random_number, and use a series of switch statements that will assign the current text version of the digit that's being examined.

My main logic flaw is in that final part, because I'm just stuck in terms of how to deal with numbers that are infinitely big, because I could probably code up to the billionth digit place holder, but there's always the possibility of dealing with random numbers that are greater than 1 billion.

So I'm basically stuck on how to actually do the math for some reason...

Anyway, here's my code thus far with a bunch of comments for myself as I go along, so forgive the mass of text between simple lines:
Code:
```// Chapt4_Exercise5.cpp : main project file.

#include "stdafx.h"
#include "math.h"

// This math.h header will allow me to use the absolute and floor functions just
// to determine the amount of digits that are present within the random_number

using namespace System;

int main(array<System::String ^> ^args)
{
/*5. Write a C++/CLI program that will generate a random integer greater than
10,000.

Output the integer and then output the digits in the integer in words.

For example, if the integer generated were 345678, then the output should be:
The value is 345678 three four five six seven eight
*/

// Create the random number generator:

Random^ random_num_gen = gcnew Random;

// Create the integer variable that we will use:

int random_number;

// Assign random number to be a random number greater than 10,000, which I
// am assuming means to literally multiply the random number we
// generate by 10,000:

random_number = 10000 * random_num_gen->NextDouble();

// Declare an integer array which we'll use to store each digit later on in
// a for loop that will examine each digit of random_number so far

// I don't know how many integers we'll actually need, so declare a place
// holder variable called n, so we can later determine the amount of digits
// in the actual random_number, and just store that in n

// All I can think of is to use Length-> but obviously, that won't work
// because I'm NOT DEALING WITH STRINGS!

// Notice how I'm using the absolute and log10 function from the math.h

// As an aside, I came to this conclusion by looking at a similar question
// on StackOverflow in which the user asked about how to determine the
// amount of digits present using the C programming language:

// Here is the link:

// http://stackoverflow.com/questions/3068397/c-how-to-find-the-length-of-an-integer

int n = floor(log10(abs(random_number))) + 1;

array<int>^ int_array = gcnew array<int>(n);

// Declare an accumulator string which I'll use later in a series of switch
// statements to add to, so that I can get a running total of the
// text versions of each digit

String ^text;

// Logic thus far, though it's really shaky honestly:

// Make a for loop that will output the current digit, so I have to
// strip each number out of an entire number, maybe put that into an array

// Then, I want to use switch statements that will output 1 through 9 for
// the current digit in text form, or at least add that text version of
// the digit to a string that acts like an accumulator:

// Maybe the only way to get each every separate digit on its own from
// random_number is to divide each number by 10, or use the mod operator
// to determine the remainder (much like an odd or even number generator
// program would act)

// The following is a skeletal for loop with switch statements, but my
// confidence is reaching its limits for some weird reason, for I feel
// this may or may not be the right approach:

for(int i = 0; i < int_array->Length; i++)
{
// Examine each place holder... oh wait, this might be impossible
// unless I make another arbitrary variable that will look at each and
// every possible placeholder...

// Exactly, what if this number I generate IS HUGE? What then? I would
// have to keep modding it by 1 million, then 1 billion, etc, until it
// would become impossible to actually predict

// So the question is, do we impose a placeholder limit on how many
// digits could exist?

// Stuck...

if(random_number % 10)
{

switch(x):

case 0:

text = text + "Zero";
break;

case 1:

text = text + "One";
break;

case 2:

text = text + "Two";
break;

case 3:

text = text + "Three";
break;

case 4:

text = text + "Four";
break;

case 5:

text = text + "Five";
break;

case 6:

text = text + "Six";
break;

case 7:

text = text + "Seven";
break;

case 8:

text = text + "Eight";
break;

case 9:

text = Text + "Nine";
break;

}

}

return 0;
}```
2. As far as the digit to text issue, all you need is a string array. Eg:
Code:
```char *digitStrings[] = {
"Zero"
, "One"
, "Two"
, "Three"
...
};```

Then you can use %10 on the integer to get the last digit, lookup the string value in that array and add it to an output string.

Code:
```int random_number=394829;
char output[1024]="";
char tmp[1024]="";

while (random_number > 0){
int digit = random_number%10;
random_number /= 10;

snprintf(tmp, 1024, "%s %s", digitStrings[digit], output);
strncpy(output, tmp, 1024);
}

printf("Output: %s", output);```
3. > random_number = 10000 * random_num_gen->NextDouble();
Have you read the manual page on NextDouble()
4. No Profile Picture
Contributing User
Devshed Newbie (0 - 499 posts)

Join Date
Aug 2013
Posts
232
Rep Power
2
Originally Posted by kicken
As far as the digit to text issue, all you need is a string array. Eg:
Code:
```char *digitStrings[] = {
"Zero"
, "One"
, "Two"
, "Three"
...
};```

Then you can use %10 on the integer to get the last digit, lookup the string value in that array and add it to an output string.

Code:
```int random_number=394829;
char output[1024]="";
char tmp[1024]="";

while (random_number > 0){
int digit = random_number%10;
random_number /= 10;

snprintf(tmp, 1024, "%s %s", digitStrings[digit], output);
strncpy(output, tmp, 1024);
}

printf("Output: %s", output);```

Cool, thanks a bunch! I think I was introduced to strncpy in the book, but I forgot about it.

Cool use of the function.

I implemented your ideas into my program, but I'm getting compilation errors because the program doesn't know what the identifiers for snprintf, strncpy, and printf, am I not including the proper header files for these functions?

Here's my code:

Code:
```// Chapt4_Exercise5.cpp : main project file.

#include "stdafx.h"

// This math.h header will allow me to use the absolute and floor functions just
// to determine the amount of digits that are present within the random_number

using namespace System;

int main(array<System::String ^> ^args)
{
/*5. Write a C++/CLI program that will generate a random integer greater than
10,000.

Output the integer and then output the digits in the integer in words.

For example, if the integer generated were 345678, then the output should be:
The value is 345678 three four five six seven eight
*/

char *digitStrings[] = { "Zero", "One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine" };

// Create a random_number integer:
int random_number = 394829;

// Create a character array that will store up to 1024 characters, but
// initialize it with an empty space for output, and another that's temporary
char output[1024] = "";
char tmp[1024] = "";

// This is a while loop that will occur as long as random_number is positive
while (random_number > 0)
{
// This creates an integer variable that will be the remainder of whatever
// the current value of randomnumber is:

// Ex: if random_number was 20, the current digit would be assigned to
// the value of 2
int digit = random_number%10;

// We will keep dividing random_number by 10 each time through the loop
// to get every possible digit
random_number /= 10;

// snprintf(): This composes a string with the same text that would be
// used with the printf function, but its contents is being stored as
// a C string in the buffer pointed by s, and takes in n as the maximum
// buffer capacity to fill)

// s = temp, n = 1024, digitStrings[digit]: is the terminating null character
// output = format parameter

snprintf(tmp, 1024, "%s %s", digitStrings[digit], output);

// strncpy: copies the first num characters of source to destination.
// If the end of the source C string is found before num characters have
// been copied (aka it stopped before completion), the destination
// string is padded with zeros until a total num characters have been
// written to it

strncpy(output, tmp, 1024);
}

printf("Output: %s", output);

return 0;
}```
5. Originally Posted by HauntJemimah
I implemented your ideas into my program, but I'm getting compilation errors because the program doesn't know what the identifiers for snprintf, strncpy, and printf, am I not including the proper header files for these functions?
You would need to include the string.h header and stdio.h header in order to use them. However, since you seem to be using .NET you may as well use it's API (ie, System::String and associated methods) rather than the older C methods I used. I'm not familiar with C++'s .NET implementation which is why I went with the classic C route.
6. No Profile Picture
Contributing User
Devshed Newbie (0 - 499 posts)

Join Date
Aug 2013
Posts
232
Rep Power
2
Originally Posted by kicken
You would need to include the string.h header and stdio.h header in order to use them. However, since you seem to be using .NET you may as well use it's API (ie, System::String and associated methods) rather than the older C methods I used. I'm not familiar with C++'s .NET implementation which is why I went with the classic C route.
I'm still receiving just one single error how my program cannot find the identifier for snprintf().

I included the "string.h" and "stdio.h" header files like you suggested but it still can't identify snprintf():
Code:
```// Chapt4_Exercise5.cpp : main project file.

#include "stdafx.h"
#include "string.h"
#include "stdio.h"

// string.h and stdio. h header files are used so that I can later use snprintf
// and strncpy

using namespace System;

int main(array<System::String ^> ^args)
{
/*5. Write a C++/CLI program that will generate a random integer greater than
10,000.

Output the integer and then output the digits in the integer in words.

For example, if the integer generated were 345678, then the output should be:
The value is 345678 three four five six seven eight

Status: Getting closer, but snprintf, strncpy, and printf are not being
recognized by the program
*/

char *digitStrings[] = { "Zero", "One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine" };

// Create a random_number integer:
int random_number = 394829;

// Create a character array that will store up to 1024 characters, but
// initialize it with an empty space for output, and another that's temporary
char output[1024] = "";
char tmp[1024] = "";

// This is a while loop that will occur as long as random_number is positive
while (random_number > 0)
{
// This creates an integer variable that will be the remainder of whatever
// the current value of randomnumber is:

// Ex: if random_number was 20, the current digit would be assigned to
// the value of 2
int digit = random_number%10;

// We will keep dividing random_number by 10 each time through the loop
// to get every possible digit
random_number /= 10;

// snprintf(): This composes a string with the same text that would be
// used with the printf function, but its contents is being stored as
// a C string in the buffer pointed by s, and takes in n as the maximum
// buffer capacity to fill)

// s = temp, n = 1024, digitStrings[digit]: is the terminating null character
// output = format parameter

snprintf(tmp, 1024, "%s %s", digitStrings[digit], output);

// strncpy: copies the first num characters of source to destination.
// If the end of the source C string is found before num characters have
// been copied (aka it stopped before completion), the destination
// string is padded with zeros until a total num characters have been
// written to it

strncpy(output, tmp, 1024);
}

printf("Output: %s", output);

return 0;
}```
Last edited by HauntJemimah; October 17th, 2013 at 01:02 PM.
7. No Profile Picture
Contributing User
Devshed Newbie (0 - 499 posts)

Join Date
Aug 2013
Posts
232
Rep Power
2
Either way, anyone know how to properly get the program to identify snprintf?
8. You misspelled it.

Read the help file (RTFM!) to see what it should be. In Visual Studio, click on the word and then press the F1 function key. After churning away for what will seem forever (AKA several seconds), the help file for _snprintf will open.

Once you have corrected the spelling, you will get warnings that you should use a safer, more secure version of that function. Read that warning completely and carefully, because it tells you the macro you need to #define that will turn off that particular warning -- it's long, so use copy-and-paste instead of typing it out.
9. No Profile Picture
Contributing User
Devshed Newbie (0 - 499 posts)

Join Date
Aug 2013
Posts
232
Rep Power
2
Nice, I should have read the documentation on that error.

Anyway, I did, and I included

at the top of my program, and it runs fine... but I still get the warning for it, hmm.

What I want to do now is to also print the actual integer though before it exists in text form.

I tried adding a Console::WriteLine after the While loop and before the statement where I use printf() at the end, but random_number is showing up as zero 0_o

EDIT: OH OH OH, I just realized it! Holy ****, I kept dividing random_number during the course of the while loop, so THAT'S WHY IT'S ZERO (wow... I can't believe I didn't see that -_-)

In that case, I'll try outputting the random_number BEFORE the while loop.

The weird thing though also to note is that my number isn't really random.

Obviously, I initialized it to some arbitrary value, but should I change it before it goes into the while loop to make it truly random?

Here's my code thus far:

Code:
```// Chapt4_Exercise5.cpp : main project file.

#include "stdafx.h"
#include "string.h"
#include "stdio.h"

// string.h and stdio. h header files are used so that I can later use snprintf
// and strncpy

using namespace System;

int main(array<System::String ^> ^args)
{
/*5. Write a C++/CLI program that will generate a random integer greater than
10,000.

Output the integer and then output the digits in the integer in words.

For example, if the integer generated were 345678, then the output should be:
The value is 345678 three four five six seven eight

Status: Getting closer, but snprintf, strncpy, and printf are not being
recognized by the program
*/

// This is the pointer to character array that will store each of the
// available digits from 0 to 9 in text form:

char *digitStrings[] = { "Zero", "One", "Two", "Three", "Four",
"Five", "Six", "Seven", "Eight", "Nine" };

// Create a random_number integer:
int random_number = 394829;

// Create a character array that will store up to 1024 characters, but
// initialize it with an empty space for output, and another that's temporary
char output[1024] = "";
char tmp[1024] = "";

Console::WriteLine(L"\nOutput: {0}", random_number);

// This is a while loop that will occur as long as random_number is positive
while (random_number > 0)
{
// This creates an integer variable that will be the remainder of whatever
// the current value of randomnumber is:

// Ex: if random_number was 20, the current digit would be assigned to
// the value of 2
int digit = random_number%10;

// We will keep dividing random_number by 10 each time through the loop
// to get every possible digit
random_number /= 10;

// snprintf(): This composes a string with the same text that would be
// used with the printf function, but its contents is being stored as
// a C string in the buffer pointed by s, and takes in n as the maximum
// buffer capacity to fill)

// s = temp, n = 1024, digitStrings[digit]: is the terminating null character
// output = format parameter

// tmp is the character array  where you want to store store the old string,
// and you want it to have a buffer capacity of 1024 character, it will look
// at the corresponding digit from the digitStrings pointer to character
// array,and store it inside the output character rarray

_snprintf(tmp, 1024, "%s %s", digitStrings[digit], output);

// strncpy: copies the first num characters of source to destination.
// If the end of the source C string is found before num characters have
// been copied (aka it stopped before completion), the destination
// string is padded with zeros until a total num characters have been
// written to it

// This is the strncpy function that will copy the contents of the tmp
// character array and store it in the output character array
strncpy(output, tmp, 1024);

}
// This will print out the digits in text form
printf("Output: %s", output);

// Add another line for space:
Console::WriteLine();

return 0;
}```
10. Mine (VS 2008) has me #define _CRT_SECURE_NO_WARNINGS; I assume that you have a later version and that it has a different macro, so no issue there.

I also kept getting warnings despite the macro, so I followed the advice either in the warnings or in the help file (click on the macro name and press F1 and then wait several seconds -- Visual Studio seems to have an awfully big overhead for some things). After I put that #define in the stdafx.h file (is for the pre-defined header), it worked and the warnings were gone. You might need to go to Build | Clean Solution to get rid of the intermediate files, such as the pre-compiled header file, in order to make it build cleanly.

The standard library functions for pseudo-random number generation (PRNG) are rand and srand. You use rand to get the next "random" integer (use the modulo operator, %, to bring it down to the range of values you want), but first you must srand once and only once to "seed" the PRNG.

The value you pass to srand is important for how "random" the values of rand will be. If you pass it the same value each time, it will generate the exact same sequence of "random" numbers each time. So the secret is to pass it a different number each time you run the program. Consider that time_t values (see time.h) are the date and time as the number of seconds that have transpired since the beginning of time, which is midnight going into 1970 Jan 01. And consider that when you call time() it returns the time_t value of the current date and time. And since every second that passes the current time will change into a value that has been unique since the beginning of time and will continue to do so until early 2038, assuming that you're still using a 32-bit time_t value (disregard if you've been upgraded to a 64-bit time_t), time(NULL) is a very common source for a unique seed for the PRNG.

Read the documentation for rand, srand, and time for the details that you will need.

It can be desirable to pass a fixed value to srand when you are testing your code which is when you would want to have the same sequence of numbers generated. Then when you are ready to release the program for users to use would be when you would be switch to closer to true randomness. Of course, when you want the sequence to be random and it isn't, then that can be confusing. Back in school in 1978, our IBM system provided BASIC, but it wasn't taught so I taught myself. My study material included David Ahl's books of classic BASIC games. Hamurabi has you managing a Mesopotamian city-state. It featured certain random events, such as plagues, which would be generated by BASIC's RND function. I coded the program as per the book, which used the Microsoft BASIC interpreter (Gates' main product before MS-DOS). First year, the plague hit. And the second, and the third, and so on. Every single time I ran it. Well, it turned out that the argument for the RND function in IBM's VS-BASIC were different than the argument for Microsoft's RND and so I was telling RND to generate the same "random" number each time. I changed the code to use the correct argument for VS-BASIC and it ran just fine.

And that is why you need to always RTFM! ("Read The Manual!").
Last edited by dwise1_aol; October 18th, 2013 at 12:03 PM.
11. No Profile Picture
Contributing User
Devshed Newbie (0 - 499 posts)

Join Date
Aug 2013
Posts
232
Rep Power
2
Originally Posted by dwise1_aol
Mine (VS 2008) has me #define _CRT_SECURE_NO_WARNINGS; I assume that you have a later version and that it has a different macro, so no issue there.

I also kept getting warnings despite the macro, so I followed the advice either in the warnings or in the help file (click on the macro name and press F1 and then wait several seconds -- Visual Studio seems to have an awfully big overhead for some things). After I put that #define in the stdafx.h file (is for the pre-defined header), it worked and the warnings were gone. You might need to go to Build | Clean Solution to get rid of the intermediate files, such as the pre-compiled header file, in order to make it build cleanly.

The standard library functions for pseudo-random number generation (PRNG) are rand and srand. You use rand to get the next "random" integer (use the modulo operator, %, to bring it down to the range of values you want), but first you must srand once and only once to "seed" the PRNG.

The value you pass to srand is important for how "random" the values of rand will be. If you pass it the same value each time, it will generate the exact same sequence of "random" numbers each time. So the secret is to pass it a different number each time you run the program. Consider that time_t values (see time.h) are the date and time as the number of seconds that have transpired since the beginning of time, which is midnight going into 1970 Jan 01. And consider that when you call time() it returns the time_t value of the current date and time. And since every second that passes the current time will change into a value that has been unique since the beginning of time and will continue to do so until early 2038, assuming that you're still using a 32-bit time_t value (disregard if you've been upgraded to a 64-bit time_t), time(NULL) is a very common source for a unique seed for the PRNG.

Read the documentation for rand, srand, and time for the details that you will need.

It can be desirable to pass a fixed value to srand when you are testing your code which is when you would want to have the same sequence of numbers generated. Then when you are ready to release the program for users to use would be when you would be switch to closer to true randomness. Of course, when you want the sequence to be random and it isn't, then that can be confusing. Back in school in 1978, our IBM system provided BASIC, but it wasn't taught so I taught myself. My study material included David Ahl's books of classic BASIC games. Hamurabi has you managing a Mesopotamian city-state. It featured certain random events, such as plagues, which would be generated by BASIC's RND function. I coded the program as per the book, which used the Microsoft BASIC interpreter (Gates' main product before MS-DOS). First year, the plague hit. And the second, and the third, and so on. Every single time I ran it. Well, it turned out that the argument for the RND function in IBM's VS-BASIC were different than the argument for Microsoft's RND and so I was telling RND to generate the same "random" number each time. I changed the code to use the correct argument for VS-BASIC and it ran just fine.

And that is why you need to always RTFM! ("Read The Manual!").
Holy ****, you taught yourself BASIC? Awesome man, that's inspiring because this is just a hobby of mine I want to use for further game development and possibly relate it to Clinical Research. Wow, just wow.

Cool game concept for BASIC too.

I learned that I should read documentation today ha ;), I'll check out the rand() functions tomorrow, thanks a bunch! (moving on to Java for an hour ;D)
12. Computer languages are like human languages, or dances (eg, Lindy, West Coast Swing, salsa). The first one is the hardest one to learn, but then after that the others get easier and easier to learn. Part of the reason is because a large part of learning that first one involves learning the basic concepts and techniques of all the others, so when you learn the second language/dance you've already learned that part and can concentrate on the particulars of that second one. Another part is that along with learning the first one you also learn how to learn one, a skill that you can then apply to learning more of them.

Let's see. I had been exposed to a little assembly in tech school, then in university they first taught me FORTRAN, then assembly, and then PL/I (IBM's super-language) which we did most of our work in until 1980 when they finally got a Pascal compiler. It was with that background (though before Pascal) that I tackled BASIC on my own, so there really wasn't anything impressive about learning it on my own.

Of course, Visual Basic has grown into a totally different animal than BASIC was, so don't confuse the two.

PS
David Ahl's books are discussed on Wikipedia at http://en.wikipedia.org/wiki/BASIC_Computer_Games. His first book is archived on-line at http://www.atariarchives.org/basicgames/, so you can see what BASIC looked like.

BASIC was all line-oriented with each line being explicitly numbered. To jump to another line such as line 300, you would GOTO 300. There was no grouping of statements, so an IF statement would be something like IF Z=11 THEN 840 and if that were true then you'd jump to line 840. Most BASIC interpreters would limit the variables to two characters, a single letter followed optionally by a single digit. But you could also use arrays, though I think the name could only be a single letter. You created a variable simply by using it. By default, a variable would be a floating-point numeric value, though you could create a character/string variable with a dollar sign which was very common (eg, \$A) and in some interpreters you could create an integer variable with a percent (eg, %I), but that was not as common.

BASIC was most commonly interpreted, which basically meant that each line had to be recompiled every time you executed it. In reality, it wasn't that bad, but programs did run more slowly than they would have if they were compiled -- later we did get BASIC compilers, eg QuickBASIC. One short-cut that they used was that every time you entered a new line, it would be parse and tokenized and analyzed for correct syntax; if the syntax was wrong, you had to correct it on the spot or else that line of code would not be accepted. That eliminated those steps when you ran the program, which sped it up a bit. That also compressed the program to fit into less memory, which was definitely a concern when most computers had about 16K of RAM. One of my old MS-DOS reference books has a table of BASIC token codes.

The late 1970's into the 1980's was the era of the "home computers", which was the first generation of consumer computers, if you exclude the electronic calculators that started coming out at the very end of the 1960's and which also used microprocessors. These home computers would be consoles that included the keyboard and with an RF output that you could connect to your TV and use the TV as your monitor. They didn't have any operating system, but rather they would boot up running the BASIC interpreter. If you entered a command without a line number, then it would immediately interpret and execute that command, but if you did give it a line number then it would be added to the program. When the first IBM PC came out circa 1980, it would boot up the PC-DOS operating system from a floppy, but if there was no floppy then it would start up the BASIC interpreter stored in ROM.

When Microsoft started up, it specialized in programming languages, especially BASIC such that Microsoft provided the BASIC interpreter for many home computers. At first, IBM only contracted them to write the programming languages for their new PC and Digital Research (famous for its microcomputer OS, CP/M) would do the operating system. When the Digital Research deal fell through (lots of urban legends about that incident), IBM asked Bill Gates if they could do the OS as well. He said yes even though they couldn't, then he bought somebody's student project, QDOS ("Quick and Dirty OS", which had apparently been based largely on CP/M, judging from the features of DOS v1), and that became the basis from which they developed PC-DOS v1.0. You can watch a lot of that history from PBS' Triumph of the Nerds: Accidental Empires (no, not the feature movie) and the TV-movie Pirates of Silicon Valley, both on YouTube. Also on YouTube you can catch PBS' sequel, Nerds 2.0.1, which concentrates more on the creation and development of the Internet -- it was produced before the .COM bubble burst, so you should take with a grain of salt their coverage of hot new .COM start-ups.
Last edited by dwise1_aol; October 18th, 2013 at 02:59 PM.