dodicat's random integral number method

General FreeBASIC programming questions.
Post Reply
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

dodicat's random integral number method

Post by deltarho[1859] »

PCG32II has recently been updated to replace the asm code used in the integral range function with, what I call, dodicat's Clng/Mod method.

In FB's Help file under Rnd we have rnd_range to give a random double in the range first to last as doubles. For a random integral number we are given a method which utilizes rnd_range.

Presented here is an overloaded rnd_range to give a random integral number using dodicat's Clng/Mod method which is twice as fast.

The code below uses 'Randomize 666' and then prints FB's value, and then we 'Randomize 666' again and then print dodicat's value. We get 305 and 136 respectively. This may seem a worry, being different, but the FB method is a linear mapping whereas dodicat's method is not; dodicat calls it a 'local range mapper, local being first and last'. I should like to come up with a better description.

The important thing is not the value of a single call but that the two methods should give, when calling many times, the same average. The next part of the code does just that and averages 10^8 calls.

It is worth noting that the FB average is 0.5 less than dodicat's average. The reason for that is because FB uses 'Int(rnd_range(first, last))' which is wrong - it should be 'Int(rnd_range(first, last)) + 0.5'.

The averages without correcting FB were 244.49028122 and 245.01891906 with the real average being 245.

Additional tests were done using a variety of first and last with negatives and so on but dodicat's method got a thorough testing before introducing it to PCG32II.

I have some code for timing random numbers which extracts the influence of a For/Next loop normally used for testing. FB's throughput was found to be 35MHz whereas dodicat's method had a throughput of 70MHz, twice as fast as FB's method.

So, if you have been using FB's 'Int(rnd_range(first, last))', even though it is wrong, give dodicat's method a try. He would like royalties but this is FreeBASIC so he will have to settle with the fame for a very ingenious method. Image

Code: Select all

'' Function to a random number in the range [first, last), or {first <= x < last}.
Function rnd_range overload (first As Double, last As Double) As Double
    Function = Rnd * (last - first) + first
End Function

'' Implements dodicat's Clng/Mod method
Function rnd_range overload (first As Long, last As Long) As Long
    Function = CLng( cast( Ulong, Rnd*2^32 ) Mod (last - first + 1) ) + first
End Function

Randomize 666
Print Int(rnd_range(69d, 421d)) ' FB
Randomize 666
Print rnd_range(69l, 421l) ' dodicat

Dim As Long i
Dim As Double tot

Randomize
Print
For i = 1 To 10^8
  tot += Int(rnd_range(69d, 421d)) ' FB
Next
tot /= 10^8
Print tot
tot = 0
For i = 1 To 10^8
  tot += rnd_range(69l, 421l) ' dodicat
Next
tot /= 10^8
Print tot

Sleep

fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: dodicat's random integral number method

Post by fxm »

At the 'Rnd' page, the examples of random functions for integer values are not wrong, but only the comments are confusing because wrongly expressed:
For the 2 lasts expressions, take into account the second comment line only.

Proposed improvement of comments:
'' Function to a random number in the range [first, last), or {first <= x < last}.
Function rnd_range (first As Double, last As Double) As Double
Function = Rnd * (last - first) + first
End Function

'' seed the random number generator, so the sequence is not the same each time
Randomize

'' prints a random number in the range [0, 1), or {0 <= x < 1}.
Print Rnd

'' prints a random number in the range [0, 10), or {0 <= x < 10}.
Print Rnd * 10

'' prints a random integral number in the range [1, 11), or {1 <= x < 11}.
'' with integers, this is equivalent to [1, 10], or {1 <= n <= 10}.

'' prints a random integral number in the range [1, 10], or {1 <= n <= 10}.
'' (because: 0 <= Rnd * 10 < 10)

Print Int(Rnd * 10) + 1

'' prints a random integral number in the range [69, 421), or {69 <= x < 421}.
'' this is equivalent to [69, 420], or {69 <= n <= 420}.

'' prints a random integral number in the range [69, 420], or {69 <= n <= 420}.
'' (because: 69 <= rnd_range(69, 421) < 421)

Print Int(rnd_range(69, 421))
From the 'rnd_range (first As Double, last As Double) As Double' function, the formula to be applied to work on 'Long' variables must be therefore:
Int(rnd_range(first, last + 1))

By using 'Int(rnd_range(69d, 421d + 1)) ' FB' in your above code, the results become good.
Last edited by fxm on Nov 07, 2019 12:59, edited 1 time in total.
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: dodicat's random integral number method

Post by deltarho[1859] »

I agree with everything except the last statement which should read:

By using 'Int(rnd_range(69d, 422d))' FB' in your above code, the results become good.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: dodicat's random integral number method

Post by fxm »

What is the difference?
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: dodicat's random integral number method

Post by deltarho[1859] »

fxm wrote:What is the difference?
Run the code with '421d+1' and then '422d'.

Try this:

Code: Select all

Print 421d+1
Print 422d
Sleep
I get

Code: Select all

4210
422
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: dodicat's random integral number method

Post by fxm »

Yes, but I wrote:
422d + 1
(with at least a space after 'd')
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: dodicat's random integral number method

Post by deltarho[1859] »

I just tried that myself, but 421d+1 should have worked.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: dodicat's random integral number method

Post by fxm »

For more information, see the Literals documentation page, section "Floating Point Literals".
.....
The letter "D" or "E", placed after the number/fraction part, allows the number to be given an exponent. The exponent may be specified as either positive or negative with a plus ("+") or minus ("-") sign. Exponents that do not have a sign are positive.
An exponent value is not required after the letter (or even after the sign), so the letter can be used on its own just to specify the type. "D" specifies a double-precision floating-point number. "E" specifies a floating-point number using the default precision.....
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: dodicat's random integral number method

Post by deltarho[1859] »

Ah, I knew of "E", which has been around since the beginning of programming, but I did not know that we could use "D" instead, which explains 421d+1 = 4120. To my mind a choice of "D" or "E" is silly, as is "&, l", "!, f", and "#, d". Using 'd' as an exponent and a 'double' is plain bonkers!

Thanks, fxm.
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: dodicat's random integral number method

Post by deltarho[1859] »

@fxm

Thanks for the 2019.11.08 update to the Help manual regarding RND, it is no longer confusing.

I think that it would be worthwhile to have a locked topic somewhere advising when an updated Help manual has been published; and, perhaps, a synopsis of changes. At the moment I call at 'Index of /stw/builds' now and then to see whether I am up to date or not.
fxm
Moderator
Posts: 12107
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: dodicat's random integral number method

Post by fxm »

On the Recently changed pages of FBWiki, there is the list of the last changes in documentation.
deltarho[1859]
Posts: 4308
Joined: Jan 02, 2017 0:34
Location: UK
Contact:

Re: dodicat's random integral number method

Post by deltarho[1859] »

I should have egg on my face but I don't do egg on my face, I just smile. Image

However, we still have to visit the Wiki as opposed to being advised of a new update.
Post Reply