PseudoRandomNumberGeneratorV2

This module is another implementation of the PseudoRandomNumberGenerator by David Eather.

Instead of the multiple files and module references used in the original, it's a single module for all data types, with the desired output set via '#option RAND_T'.

It supports data types byte, word, longword, or float, and adds a new overloaded function GetRND(lower, upper) that returns a random number within the range (lower, upper)

See the original wiki page for details on the random number generation algorithm.

Usage

Set the desired random number datatype using '#option RAND_T' and include the file rand.bas. That's it.

Here's an example for a random byte (the default)

// select random number type... supports byte, word, longword, or float
#option RAND_T = byte
include "rand.bas"  

dim Y as RAND_T		' Y is a byte in this example 

rand.InitializeRND(123)	' set new seed (optional)

while (true)
  Y = GetRND()		' get a random number 0-255 (byte value, as set by RAND_T)
  Y = rand.GetRND(1, 5)	' get a random byte in the range of 1 to 5
end while

To get different random number types, simply change '#option RAND_T = ' to match the data type you want... ie '#option RAND_T = word' or '#option RAND_T = float'

Rand.bas Module

This is the module code for the above example. Just copy and paste into the Swordfish IDE and save in your UserLibrary folder as "rand.bas"

{
*****************************************************************************
*  Name    : rand.bas                                                       *
*  Author  : David Eather (modified by Jerry Messina)                       *
*  Notice  : This code is placed into the Public Domain                     *
*  Date    : 11 APR 2023                                                    *
*  Version : 1.1  11 APR 2023 JM                                            *
*          :    - based on v1.0 code by David Eather                        *
*          :    - unify code to single file                                 *
*          :    - set GetRND() return value type using '#option RAND_T'     *
*          :    - add longword support                                      *
*          :    - add overloaded GetRND function to get number in a range   *  
*          :        GetRND(lower as RAND_T, upper as RAND_T) as RAND_T      *
*          : 1.0  19/07/2011                                                *
*          :    - original version by David Eather                          *
*****************************************************************************
}
module rand

// select GetRND() random number data type - byte, word, longword, or float
#option RAND_T = byte
#if not(RAND_T in (byte, word, longword, float))
  #error RAND_T, "RAND_T type not supported"
#endif  

#if (RAND_T = byte)
  type RAND_T = byte
  type RAND_RESEED_T = byte
  dim GLFSR as byte = 1
  dim LCG   as byte = 84
  const LCG_MASK  as byte = $FF
  const GLFSR_TAP as byte = 135
  const GLFSR_OR  as byte = $80
#elseif (RAND_T = word)
  type RAND_T = word
  type RAND_RESEED_T = word
  dim GLFSR as word = 1
  dim LCG   as word = 21844
  const LCG_MASK  as word = $FFFF
  const GLFSR_TAP as word = 447
  const GLFSR_OR  as word = $8000
#elseif (RAND_T = longword)
  type RAND_T = longword
  type RAND_RESEED_T = longword
  dim GLFSR as longword = 1
  dim LCG   as longword = 5592404
  const LCG_MASK  as longword = $FFFFFFFF
  const GLFSR_TAP as longword = 7679
  const GLFSR_OR  as longword = $80000000
#elseif (RAND_T = float)
  type RAND_T = float
  type RAND_RESEED_T = longword
  dim GLFSR as longword = 1          // only 24-bits are used (mantissa)
  dim LCG   as longword = 5592404
  const LCG_MASK  as longword = $00FFFFFF   // limit value to 16777215
  const GLFSR_TAP as longword = 7679
  const GLFSR_OR  as longword = $01000000
#endif

// get a random number
//  return value based on option RAND_T:
//  byte = 0 to 255
//  word = 0 to 65535
//  longword = 0 to 4294967295
//  float = 0.0 to 0.9999999
public function GetRND() as RAND_T
    // LCG
  #if (RAND_T = byte)
    LCG = byte(LCG * 7) + 17 
  #elseif (RAND_T = word)
    LCG = word(LCG * 127) + 259
  #elseif (RAND_T = longword)
    LCG = (LCG * 2047 + 4091)
  #elseif (RAND_T = float) 
    LCG = (LCG * 2047 + 4091) and LCG_MASK
  #endif

    // Galios LFSR
    if (GLFSR.bits(0) = 1) Then
        GLFSR = GLFSR xor GLFSR_TAP
        GLFSR = (GLFSR >> 1) or GLFSR_OR
    else
        GLFSR = (GLFSR >> 1)
    endif

  #if (RAND_T = float)
    result = (LCG xor GLFSR) and $7FFFFF    // limit float value to 0.999999
    result = result / $800000 
  #else
    result = (LCG xor GLFSR)
  #endif
end function

// get a random number in the range (lower, upper)
// overload
public function GetRND(lower as RAND_T, upper as RAND_T) as RAND_T
  #if (RAND_T = float)
    dim f, range as float
    f = GetRND()            // 0 - 0.9999
    range = upper - lower
    result = (f * range) + lower
  #else     
    result = (GetRND() mod (upper - lower + 1)) + lower
  #endif
end function

// reseed the random number generator
public sub InitializeRND(reseed as RAND_RESEED_T)
    LCG = reseed and LCG_MASK
    // make the start values very different - not really important
    // except that GLFSR must not be zero
    GLFSR = LCG xor ($55555555 and LCG_MASK)
    if (GLFSR = 0) then
        GLFSR = 1 
    endif
end sub

end module