No announcement yet.

Rnd2 disscussion

  • Filter
  • Time
  • Show
Clear All
new posts

  • When the above was first posted I forgot to mention that CASE 6 and CASE 7 reference to 'GetCrypto.Bytes(rndE)' had changed to
    If SpeedyCrypto Then
    End If
    I'm new to objects. It hadn't dawned on me that we could have two classes with the same interface name. Having done that CASE 6 and CASE 7 stand as was. Aren't objects something else?


    • Hi John. We posted close to the same time.

      SpeedyCrypto is no longer needed - see my last post.

      The Bytes Method in the Class CSpeedyCryptoRand is over three times faster than the Bytes Method in the Class CCryptoRand.

      That is not a "big chunk of code" - cor blimey!

      If we do a dll then it will work for '95 to Server 2008. Don't forget a lot of folk still use Win 2000.

      What do you mean "after the 1st call?".
      Last edited by David Roberts; 21 Oct 2008, 08:45 AM.


      • However, when it comes to a randomised seed I'll be going the crypto root. Whoever gets to the pub first buys the first round.
        That's cool, that's cool... we've got the capability and it's programmers choice. However, once seeded, further calls to Rnd() will be entirely deterministic. This is at least partially true too for getCrypto, tho you've shown previously it sometimes gets reseeded in XP+ at unknown intervals. Rnd(1) does its best to remove that determinism.


        • I'm getting confused here.

          You use Rnd2(1) for two purposes: One as a seeding mechanism only, followed by MWC; two as a generator for something like your dice throwing example. With crypto usage we'd use Rnd2(6) for seeding and Rnd2(7) for the dice example; although I wouldn't - I'd use MWC for the dice example as well since I take the view that cryptographic random numbers are for passwords and the like and not for simulation and the like - except for seeding, of course.
          Last edited by David Roberts; 21 Oct 2008, 09:40 AM.


          • re. post 144: So we can say the crypto bytes are going to be used relatively sparingly. Therefore, I think having the two speed options isn't really needed since 3x won't be noticed over just a few thousand bytes of generated data. A problem with the new code is that placing the IF...THEN clause in CASE 6 causes, get this, a 25% slowdown generating the completely unrelated rnd2Long, rnd2(range) and rnd2() numbers. I have no idea why such a big change occurs. That is, the whole function slows 25% from just those few lines.

            added: Btw, if the function is in a Dll, will none of the initialization/release code be needed in PBMAIN?
            Last edited by John Gleason; 21 Oct 2008, 10:39 AM.


            • So we can say the crypto bytes are going to be used relatively sparingly.
              With my modus operandi, yes, but some folk may want to crypto re-seed often in a similar way as you would Rnd2(1) re-seed often. I cannot think why but the additional code accommodates all manner of uses and not just my m.o. or yours.
              That's cool, that's cool..
              When someone says/writes 'that's cool' twice that tells me that they are trying to convince themselves they are cool.

              I introduced crypto as an alternative to your Rnd2(1) for users of Xp/Vista. When I introduced a non-XP/Vista version of crypto I wrote that for you. You then changed it to a generator as opposed to a seeder and included it in the new version of the include file. So, the new include file did not have a crypto seeder. Gary, for one, preferred the crypto seeder over Rnd2(1).

              A problem with the new code is that placing the IF...THEN clause in CASE 6
              I hadn't tested with that but it is academic now according to post #141's last line. Post #141 is confusing. The Code section had been forgotten in post #139 but it was no longer required anyway. When we use 'GetCrypto.Bytes' in post #139's revision the Interface used is the same but the Class used depends upon the OS platform. [1] I've just run the code and there are no speed issues.

              With regard a dll how about this:
              Just before '%USECRYPTO = 1 'If you have.....' we can insert
              #If %Def(%Pb_Cc32)
                #If (%Pb_Revision And &H0FF00) >= &H0500
                  %USECRYPTO = 1
                   %USECRYPTO = 0
                #If (%Pb_Revision And &H0FF00) >= &H0900
                  %USECRYPTO = 1
                  %USECRYPTO = 0
              and then remove '%USECRYPTO = 1 'If you have.....'

              So, the crypto features are now optional at runtime, as opposed to compile time, but only if PB9 or CC5. If users have XP/Vista they get the fast crypto whether in practice they need the extra speed or not. "No one will ever need more than 640Kb of RAM"?. What a gem.

              [1] That, of course, is how the API works. Folks with a god working knowledge of objects must be in seventh heaven with PB9 and CC5.
              Last edited by David Roberts; 21 Oct 2008, 12:47 PM. Reason: Added [1]


              • I changed the os ver. code a little because it wasn't assigning the object if you don't have winNT. Timing looks good tho, same as before.

                Welp, except for "philosophical differences" re. Rnd(1), we're ready for the consolidated post I do believe.

                #IF %USECRYPTO
                  LOCAL os AS OSVERSIONINFO, strOS AS STRING
                  GetVersionEx os
                  IF os.dwPlatformId = %VER_PLATFORM_WIN32_NT THEN
                    strOS = TRIM$(STR$(os.dwMajorVersion)) & "." & TRIM$(STR$(os.dwMinorVersion))
                    IF strOS > "5.0" THEN ' we have XP/Vista/Server 2003 or 2008
                      LET GetCrypto = CLASS "CSpeedyCryptoRand"
                      LET GetCrypto = CLASS "CCryptoRand"
                    END IF
                    LET GetCrypto = CLASS "CCryptoRand"
                  END IF


                • Well caught, John.

                  we're ready for the consolidated post I do believe.
                  Yes, I do believe that is so.


                  • Just for other folk who haven't had the time to keep up with all the changes of late here is were we are right now with an XP box.

                    Forcing the slower crypto to execute, that 101.42ms x50 falls to 420.24ms x50.

                    I think I must have done a run with the two separate Interfaces because the factor is 4 and not 3. Weird one that.
                    Last edited by David Roberts; 21 Oct 2008, 03:47 PM. Reason: syntax


                    • I added a few macros for simplicity. Instead of having to copy the code at the top & bottom of PBMAIN to use crypto, just use the macro names Rnd2goCrypto and Rnd2stopCrypto at the top and bottom respectively.

                      Oh, and there's those macro names; I was hoping maybe Gary would chime in but not yet. I'm still for Rnd2 or Randomise at the front. You can always start typing and then decide which you want. Randomise... Rnd2... You know right away it's a Ran Rnd something. That's why it gets my vote.:arco:

                      ' used for Rnd2() function and macros. "Rnd2TrueCrypto"
                      %USECRYPTO = 1  'If you have a PB version earlier than ver9/5, or if you don't need to use the
                                      'Crypto features, set to zero. If using Crypto features, place the statement
                                      'Rnd2goCrypto at the top of PBMAIN, and the statement Rnd2stopCrypto at the bottom.
                      MACRO Rnd2goCrypto       'Rnd2goCrypto to be placed at the top of PBMAIN
                        #IF %USECRYPTO
                          LOCAL os AS OSVERSIONINFO, strOS AS STRING
                          GetVersionEx os
                          IF os.dwPlatformId = %VER_PLATFORM_WIN32_NT THEN
                            strOS = TRIM$(STR$(os.dwMajorVersion)) & "." & TRIM$(STR$(os.dwMinorVersion))
                            IF strOS > "5.0" THEN ' we have XP/Vista/Server 2003 or 2008
                              LET GetCrypto = CLASS "CSpeedyCryptoRand"
                              LET GetCrypto = CLASS "CCryptoRand"
                            END IF
                            LET GetCrypto = CLASS "CCryptoRand"
                          END IF
                      END MACRO
                      MACRO Rnd2stopCrypto     'Rnd2stopCrypto to be placed at the bottom of PBMAIN
                        #IF %USECRYPTO
                          GetCrypto = NOTHING
                      END MACRO
                      DECLARE FUNCTION CryptAcquireContext LIB "advapi32.dll" ALIAS "CryptAcquireContextA" _
                            ( hProv AS LONG, zContainer AS ASCIIZ, zProvider AS ASCIIZ, _
                               BYVAL dwProvType AS DWORD, BYVAL dwFlags AS DWORD ) AS LONG
                      DECLARE FUNCTION CryptReleaseContext LIB "advapi32.dll" ALIAS "CryptReleaseContext" _
                            ( BYVAL hProv AS LONG, BYVAL dwFlags AS DWORD ) AS LONG
                      DECLARE FUNCTION CryptGenRandom LIB "advapi32.dll" ALIAS "CryptGenRandom" _
                            ( BYVAL hProv AS LONG, BYVAL dwLen AS DWORD, pbBuffer AS BYTE) AS LONG
                      DECLARE FUNCTION RtlGenRandom( RandomBuffer AS BYTE, BYVAL RandomBufferLength AS DWORD ) AS LONG
                      #IF %USECRYPTO
                        GLOBAL GetCrypto AS ICryptoRand
                        CLASS CCryptoRand
                          INSTANCE hProv AS DWORD
                          CLASS METHOD CREATE
                            LOCAL Prov_Rsa_Full, Flags AS DWORD
                            DIM MS_Def_Prov AS ASCIIZ * 50
                            Prov_Rsa_Full = 1
                            Flags = &hF0000000
                            MS_Def_Prov = "Microsoft Base Cryptographic Provider v1.0"
                            CryptAcquireContext( hProv, BYVAL %Null, BYVAL %Null, Prov_Rsa_Full, Flags )
                            CryptAcquireContext( hProv, BYVAL %Null, MS_Def_Prov, Prov_Rsa_Full, Flags )
                          END METHOD
                          CLASS METHOD Destroy
                            CryptReleaseContext hProv, 0
                          END METHOD
                          INTERFACE ICryptoRand: INHERIT IUNKNOWN
                            METHOD Bytes( x AS EXT) AS LONG
                              DIM BinaryByte(9) AS STATIC BYTE ' John Gleason tip
                              CryptGenRandom( BYVAL hProv, 10,  BinaryByte(0))
                              x = PEEK( EXT, VARPTR( BinaryByte(0)))
                            END METHOD
                          END INTERFACE
                        END CLASS
                        CLASS CSpeedyCryptoRand
                          INSTANCE hLib, hProc AS DWORD
                          CLASS METHOD CREATE
                            hLib = LoadLibrary( "advapi32.dll")
                            hProc = GetProcAddress(hLib, "SystemFunction036")
                          END METHOD
                          CLASS METHOD Destroy
                            FreeLibrary hLib
                          END METHOD
                          INTERFACE ICryptoRand: INHERIT IUNKNOWN
                            METHOD Bytes(x AS EXT) AS LONG
                              DIM BinaryByte(9) AS STATIC BYTE ' John Gleason tip
                              CALL DWORD hProc USING RtlGenRandom( BinaryByte(0), 10 )
                              x = PEEK( EXT, VARPTR( BinaryByte(0)))
                            END METHOD
                          END INTERFACE
                        END CLASS
                      ' Rnd2 Macros
                      MACRO Randomise       = Rnd2(1)                       'with an "s" to distinguish from RANDOMIZE
                      MACRO RandomiseTru    = Rnd2(1)                       'a fun way to think of it :D Same as Randomise
                      MACRO RandomiseCrypto = Rnd2(6)                       'cryptographically seed the random number generator
                      MACRO Rnd2Long        = Rnd2(-2147483648, 2147483647) 'commonly used full LONG range
                      MACRO Rnd2Crypto      = Rnd2(7)                       'cryptographically secure randoms
                      ' Rnd2 Additional Macros
                      MACRO RepeatLastRnd2     = Rnd2(0)
                      MACRO ToggleScope        = Rnd2(2)                    'turn on/off EXT random range -1 to 1 exclusive,
                      MACRO DefaultSequence    = Rnd2(3)
                      MACRO RepeatLastSequence = Rnd2(4)
                      MACRO GotoBookmark       = Rnd2(4)
                      MACRO Bookmark           = Rnd2(5)
                      MACRO TSCplusQPC
                         !cpuid        ;serialize
                         !dw &h310f    ;read time stamp counter
                         !ror eax, 1   ;smooth because some processors are always even low bit
                         !mov rndL,eax ;save because QPC overwrites registers
                         QueryPerformanceCounter eq
                         !mov eax, eq  ;LO dword
                         !ror eax, 1   ;smooth, low bit might always be even on some cpu's
                         !add eax, rndL;smoothed QPC + saved smoothed TSC
                      END MACRO
                      FUNCTION Rnd2( OPT One AS LONG, Two AS LONG ) AS EXT
                        'I modified this function in an attempt to enhance Dave Robert's comprehensive Rnd2 posted earlier here by making it
                        '1) as exactly like RND as possible, 2) as fast as possible, while maintaining 3) random no matter how initiated & used.
                        'This function can replace PowerBasic RND function entirely I believe, and has almost the same syntax.
                        'A major difference between Rnd2 and RND is that Rnd2 has TrueRandom© ;) initialization. Say what? Well read on.
                        'What are TrueRandom numbers?:
                        'Some say true randomness doesn't exist, but eg. radioactive decay makes a strong case for true randomness. So Rnd2
                        'uses the natural radioactive decay of gallium arsenide isotopes in your CPU to create TrueRandom numbers.
                        'Hey, just kidding. TrueRandom numbers actually get their randomness from slight timing variations that occur during
                        'eg. memory fetches, context switching, interrupt calls, hard drive head seeks--really anything that takes slightly
                        'varying amounts of time on your computer, which is actually...everything! TrueRandom then multiplies these "jitter"
                        'differences HUGELY, and also accumulates them so they compound one another. This "time static" let's call it, works
                        'its magic and voila!, statistically random real-time numbers. The technique to code for TrueRandom numbers is shown
                        'in the dice example below (see "1 argument").
                        'Floating point mode:
                        'Rnd2 returns a random value that is less than 1, but greater than or equal to 0. Numbers generated by Rnd2 CAN
                        'be "really" random, or can apply a pseudo-random transformation algorithm to a starting ("seed") value. Given the
                        'same seed, Rnd2 always produces the same sequence of "random" numbers. The random value is calculated
                        'internally as an extended precision representation so it can be readily used in any situation. Rnd2 has a period of
                        'at least 2^61, and if you re-seed it every few hundred years, up to ~2^70. That's 200 billion times larger than
                        'RND's period of ~2^32. All 18 digits of Rnd2 are significant.
                        'Integer Range mode:
                        'Rnd2(a, b) returns an extended value equal to a Long-integer in the range of a to b inclusive.
                        'a and b can each be a numeric literal or a numeric expression that evaluates within the range of a Long-integer
                        '(-2,147,483,648 to 2,147,483,647). The fastest mode of Rnd2 is Rnd2(-2147483648, 2147483647) which is equal to
                        'the MACRO Rnd2Long for convenience. All digits/bytes/bits in integer mode are significant.
                        'Special effects mode:
                        'When used with a single LONG numeric expression argument, the value returned by Rnd2 depends on the optional LONG
                        'numeric value you supply as the argument, as follows:
                        'No argument: Rnd2 generates the next number in sequence based on the initial seed value.
                        '0 argument : Rnd2 repeats the last number generated.
                        '- argument : negative argument causes the random number generator to be re-seeded and returns the first value in the
                        '             new sequence. Subsequent uses of Rnd2 with no argument continues the new sequence.
                        '             Different neg LONG arguments guarantee non-duplicate sequences for each integer -1 thru -2147483648
                        '             so you can select your own repeatable sequences from the 2Gig available. This gives a similar
                        '             functionality to the PB statement "RANDOMIZE number" but without dupe sequences.
                        'o-o--o-o--o The following arguments 1 thru 5 are special Rnd2 effects o--o-o--o-o--o-o--o-o--o-o--o-o--o-o--o-o--o-o
                        '1 argument : re-seeds the generator in a "truly random" way so its next number will be eg. like really throwing dice.
                        '             More calls with no argument use the internal pseudo-random algorithm to continue the series, HOWEVER,
                        '             unlike RND, you can call Rnd2(1) aka. Randomise/RandomiseTru over and over at will to keep generating real
                        '             random numbers. For example:
                        '               DO: Rnd2(1): ? STR$(Rnd2(1, 6)): Rnd2(1): ? STR$(Rnd2(1, 6)): LOOP
                        '               or its equivalent:
                        '               DO: Randomise: ? STR$(Rnd2(1, 6)): Randomise: ? STR$(Rnd2(1, 6)): LOOP
                        '               or also equivalent:
                        '               DO: RandomiseTru: ? STR$(Rnd2(1, 6)): RandomiseTru: ? STR$(Rnd2(1, 6)): LOOP
                        '             is effectively just like rolling real dice in real time with no dependency on any pseudo-random algorithm!
                        '             Actually, real rolled dice would likely have to be manufactured with ultra precision to statistically
                        '             match the distribution of the above code. :o) Rnd2(1) uses ~2500 tix per call. Note that after calling
                        '             Rnd2(1), ONLY the very next Rnd2() call with either no parameters, or with a 2-parameter integer range
                        '             will be TrueRandom©. Further calls without re-seeding become RAPIDLY DETERMINISTIC.
                        '2 argument : Toggles Rnd2 to return a random value that is greater than -1 and less then 1, represented (-1,1).
                        '             Next use toggles back to less than 1, but greater than or equal to 0, ie. [0,1).
                        '3 argument : Rnd2 returns its default sequence.
                        '4 argument : repeat last sequence or bookmarked sequence.
                        '5 argument : bookmark the position in current sequence.
                        '6 argument : Generates a cryptographically secure random seed value for the random number generator. ONLY the very
                        '             next Rnd2() call with either no parameters, or with a 2-parameter integer range will be secure. Further
                        '             calls without re-seeding become RAPIDLY INSECURE.
                        '7 argument : Generates cryptographically secure random numbers as EXT values. Rnd2(7) can be called just like Rnd2(),
                        '             but can never be bookmarked or have a sequence repeated. Rnd2(0) will, however, repeat the last random
                        '             generated even if it was made by Rnd(7).
                        '>7 argument: Rnd2 generates the next number in sequence based on the initial seed value.
                        'Do not use 0, 2 thru 5, or negative value arguments in special effects mode unless you are looking for the special
                        'effects those argument values produce. And remember Rnd2(1) uses ~2500 tix doing its tuff task.
                        'The random number generator can be reset back to the default seed using the following statement: Rnd2(3)
                        'Try as we might to make it faster, in PB9, Rnd2 with no arguments is about the same speed as RND. But take heart,
                        'because Rnd2(-2147483648, 2147483647) is ~110% faster!
                        'Reasons to use Rnd2:
                        '1)Will likely never repeat a sequence even if run for years--hundreds of years. Has a big period.
                        '2)Integer mode is much faster.
                        '3)Works almost just like RND so quick learning curve.
                        '4)Can use RandomiseTru/Randomise/Rnd(1) whenever the heck fire you please, eg. to make TrueRandom numbers. Rnd2 is
                        '  very forgiving, so no matter what you do (almost), you are going to get the fine random sequences you imagined.
                        '5)Can return +/- EXT randoms using Rnd2(2)/ToggleScope to switch it on/off.
                        '6)All digits are statistically random; they pass all known tests for randomness.
                      #REGISTER NONE
                      STATIC oneTime, rndL AS LONG, eq AS QUAD
                      STATIC storeState AS STRING * 12
                      STATIC mwcSoph AS DWORD, rndE AS EXT
                      STATIC mwcRandom, mwcCarry, TogScope AS LONG  'mwc prefix means "multiply with carry" random generator based on theory
                                                                    'by George Marsaglia and is the fastest generator known to man. A man. Me. ;)
                        !lea eax, Two
                        !mov ecx, [eax]
                        !cmp ecx, 0
                        !jne twoParams      ;if optional parameter Two passed, belt down to integer Rnd2 code section
                        !lea eax, One
                        !mov ecx, [eax]
                        !cmp ecx, 0
                        !je noParams        ;if no params, boogie down to noParams code
                        'we got here so there must be 1 parameter
                              SELECT CASE AS LONG One
                                CASE 1          ' Randomise. tot possible unique sequences = &hffffffff * &hedffffff * 384
                                                ' which = 6.6 * 10^21. This excess of initial sequences allows you to
                                                ' freely use RandomiseTru/Randomise/Rnd(1) at will to give TrueRandom numbers.
                                   oneTime = 1
                                   TSCplusQPC              'add time stamp counter and queryPerfCounter, both !ror 1
                                   !mov ecx, 384           ;384 possible sophie-germain multipliers
                                   !mov edx, 0             ;clear edx for division
                                   !div ecx                ;random remainder (MOD 384) in edx now
                                   !lea ecx, multiplier    ;codeptr to 384 multipliers
                                   !mov ecx, [ecx+edx*4]   ;got rnd soph germain multiplier now
                                   !mov mwcSoph, ecx       ;save it
                                   SLEEP 0                 'considered QPC too because rarely, this can slow 20x, but only sleep 0 passed diehard.
                                   TSCplusQPC              'add time stamp counter and queryPerfCounter, after both !ror 1
                                   !mov mwcRandom, eax     ;save. Now make sure mwcCarry <> mwcSoph(from above) - 1 or 0.
                                   !cmp mwcCarry, 0        ;if value is already present, use it as a "memory" of previous loops
                                   !ja short mwcCarNot0
                                   SLEEP 0
                                   TSCplusQPC              'add time stamp counter and queryPerfCounter, both !ror 1
                                   !mov mwcCarry, eax      ;4 billion+ carrys now possible
                                   !jnc short mwcCarNot0   ;ok if <> 0 after add, but if there's a carry, add a constant to be sure it's <> 0
                                   !mov mwcCarry,&h1234abcd;it was zero! so make it your choice of a constant
                                   !mov ecx, mwcSoph
                                   !sub ecx, 1             ;mwcSoph - 1
                                   !cmp mwcCarry, ecx      ;is carry >= mwcSoph - 1 ?
                                   !jb  short mwcAllOk     ;if not, we're done
                                   !and mwcCarry,&h7fffffff;make it less than mwcSoph - 1
                                   GOSUB gsRandomLong ' Shuffle mwcRandom a bit more and shuffle mwcCarry
                                   GOSUB gsStoreSeq
                                CASE 0            ' Repeat the last number generated
                                  FUNCTION = rndE ' will, of course, be zero if no rndE calculated yet
                                  EXIT FUNCTION
                                CASE 2           'make it 50/50 + and -
                                  TogScope = NOT TogScope
                                CASE <= -1       ' Guarrantees a non-duplicate sequence for each integer -1 thru -2147483648
                                                 ' so you can select your own repeatable sequences from the 2Gig available. This gives a
                                                 ' similar functionality to the PB statement "RANDOMIZE number" but without dupe sequences.
                                  mwcSoph = PEEK(DWORD, CODEPTR(multiplier) - (One MOD 384) * 4)
                                  mwcRandom = &h55b218da - One 'assures maximum possible 2147483648 unique sequences
                                  mwcCarry  = &h3fe8700c
                                  GOSUB gsStoreSeq
                                  oneTime = 1
                                  GOTO noParams
                                CASE 3          ' DefaultSequence
                                   GOSUB gsDefaultSeq
                                CASE 4          ' RepeatLastSequence/gotoBookmark
                                  IF mwcSoph = 0 THEN ' there wasn't a last sequence so use default
                                    GOSUB gsDefaultSeq
                                  ELSE                ' read saved values from storeState string.
                                    mwcSoph    = PEEK(DWORD, VARPTR(storeState    ))
                                    mwcRandom  = PEEK( LONG, VARPTR(storeState) + 4)
                                    mwcCarry   = PEEK( LONG, VARPTR(storeState) + 8)
                                  END IF
                                CASE 5          ' Bookmark
                                  GOSUB gsStoreSeq
                      #IF %USECRYPTO
                                CASE 6                   ' RandomiseCrypto
                                  oneTime = 1
                                  !fld tbyte rndE        ;save rndE for a moment
                                  GetCrypto.Bytes(rndE)  'Use rndE as a temporary buffer and populate with 10 cryptographic bytes.
                                  mwcRandom = PEEK(DWORD, VARPTR(rndE))
                                  mwcCarry  = PEEK(DWORD, VARPTR(rndE) + 4)
                                  mwcSoph   = PEEK( WORD, VARPTR(rndE) + 8)
                                  mwcSoph = PEEK(DWORD, CODEPTR(multiplier) + (mwcSoph MOD 384) * 4) 'get soph prime
                                  IF mwcCarry = 0 GOTO reSeedCrypt           'odds are less than 1 in 4.2 billion
                                  IF mwcCarry = mwcSoph - 1 GOTO reSeedCrypt '  "          "           "
                                  !fstp rndE             ;restore rndE
                                  GOSUB gsStoreSeq
                                CASE 7                   ' Rnd2Crypto
                                  GetCrypto.Bytes(rndE)  ' Get 10 cryptographic bytes as an ext
                                  POKE QUAD, VARPTR(eq), PEEK(QUAD, VARPTR(rndE)) 'POKE 8 of them to eq
                                  GOTO okQuadRnd
                                CASE ELSE
                                  GOTO noParams          ' + number > 6, so just generate another EXT rnd
                              END SELECT
                            FUNCTION = 0 ' OK
                            EXIT FUNCTION
                            gsDefaultSeq:                'gs prefix means this is a GOSUB sub
                              oneTime = 1
                              mwcSoph = 4221732234
                              mwcRandom = &ha5b218da     ' can be any LONG except &hffffffff and 0
                              mwcCarry  = &h3fe8700c     ' can be any number except (mwcSoph-1) and 0
                              GOSUB gsStoreSeq
                            gsStoreSeq:                  'save original starting point of sequence in 12-byte storeState string. This is more
                                                         'efficient than creating three separate variables with higher function-call overhead.
                              !lea eax, storeState       ;str address
                              !mov ecx, mwcSoph
                              !mov edx, mwcRandom
                              !mov [eax+0], ecx          ;save mwcSoph in storeState str.
                              !mov ecx, mwcCarry
                              !mov [eax+4], edx          ;save mwcRandom
                              !mov [eax+8], ecx          ;save mwcCarry
                        twoParams:   ' both optional parameters passed so it's an integer range which can be quickly calculated...
                            IF oneTime = 0 THEN GOSUB gsDefaultSeq
                            !push ebx
                            !mov eax, One
                            !mov ebx, Two
                            !mov eax, [eax]      ;dereference One
                            !mov ebx, [ebx]      ;   "        Two
                            !cmp eax, ebx        ;is One > Two?
                            !jg short oneGtTwo   ;jump if One > Two
                            !sub ebx, eax        ;now ebx is range
                            !cmp ebx, &hffffffff ;is it max?
                            !jne short rangeNotMax
                            !jmp short doTheRnd
                           oneGtTwo:             'swap and do like above
                                                 'Thanks Gary Ramey for pointing out we don't want to change the value of parameter One...
                            !mov edx, ebx        ;copy range to ebx
                            !sub eax, ebx        ;now eax holds range
                            !cmp eax, &hffffffff ;max?
                            !mov ebx, eax        ;save eax because gsRandomLong overwrites it. Now ebx holds range
                            !mov eax, edx        ;eax contains Two now
                            !jne short rangeNotMax
                            'above asm does this:
                      '     IF One > Two THEN use Two as low bound instead of One,
                      '     so range = Two - One or range = One - Two depending.
                      '     IF range <> &hffffffff GOTO rangeNotMax
                            !pop ebx
                            GOSUB gsRandomLong   'get a rndL
                            !mov rndL, eax       ;range is max, ie. any LONG integer is wanted, so since that is just what gsRandomLong does...
                            !fild rndL           ;give the function the direct feed. This is now by far the fastest return from Rnd2.
                            !fld st(0)
                            !fstp rndE           ;save for repeat last random--Rnd2(0)
                            !fstp FUNCTION       ;save to FUNCTION
                            EXIT FUNCTION
                            !mov rndL, eax       ;save low bound from above
                            rndE = .5            'ok as a single, stored as exactly .5000000000000000000
                            !add ebx, 1          ;we want range to be inclusive, eg. Rnd2(1,6) gives 1,2,3,4,5,6 evenly randomized
                            GOSUB gsRandomLong   'get a random into eax
                            !fld rndE
                            !lea ecx, rndE
                            !lea edx, eq
                            !mov dword ptr[ecx],    &hffffffff ;save 2.328306436538696289e-10 in rndE--EXT image of 1 / 4294967296. <<max DWORD+1
                            !mov dword ptr[ecx+4],  &hffffffff ;             "
                            !mov  word ptr[ecx+8],  &h3fde     ;             "
                            !mov [edx], eax        ;save the random DWORD in LO(eq)
                            !mov dword ptr[edx+4],0;zero HI(DWORD, eq)
                                                   'Locked and loaded. We have ebx=range, eq=rand dword, rndE=divisor, so
                                                   'the following asm will do: FUNCTION = rand * (1 / 4294967296) * range
                            !fld rndE              ;load 1 / 4294967296 into float reg
                            !fild qword eq         ;load rand DWORD into float register
                            !fmulp st(1),st(0)     ;effectively rand dword / 4294967296 without division
                            !mov eq, ebx           ;save the range in ebx to LO(eq). HI(eq) is zero already
                            !fild qword eq         ;load range DWORD into float register
                            !fmulp st(1),st(0)     ;now makes range a rand % of original range
                            !fsub st(0),st(1)      ;sub .5 so when it rounds, high and low bounds are correct
                            !fistp eq              ;save in eq. only LO(eq) will be used
                            !mov ecx, rndL         ;get low range bound
                            !fstp st(0)            ;pop .5 off
                            !mov edx, eq           ;ok because LO only used
                            !add ecx, edx          ;add random value to it within range specified. This is: rndL = One + range
                            !mov rndL,ecx          ;finish rndL = One + range
                            !pop ebx
                            !fild rndL
                            !fld st(0)
                            !fstp rndE           ;save for repeat last random--Rnd2(0)
                            !fstp FUNCTION       ;save to FUNCTION
                            EXIT FUNCTION
                       noParams:     ' no optional parameters passed, so calculate EXT rnd...
                        IF OneTime = 0 THEN GOSUB gsDefaultSeq ' otherwise start crunching
                        GOSUB gsRandomLong 'sub to generate LONG rnd value
                        !lea ecx,   eq
                        !mov [ecx], eax           ; = POKE LONG, eqPtr,     rndL
                        GOSUB gsRandomLong 'sub to generate LONG rnd value
                        !lea ecx,     eq
                        !mov [ecx+4], eax         ; = POKE LONG, eqPtr + 4, rndL
                        okQuadRnd:                'now we have a complete rand quad in eq
                          !cmp togScope, 0        ;do we need to make it 50/50 + and - ?
                          !jne short rndNeg       ;jump if we do.
                          !lea ecx, eq            ;eq ptr
                          !and dword ptr[ecx+4], &h7fffffff ;only positive [0,1) is needed, so make eq +
                          !fild qword eq          ;float load eq which is now a complete quad random
                          '---------------------  'now take the quad and divide by 9223372036854775816, and it perfectly ranges from [0,1) or (-1,1)
                          !lea ecx, rndE          ;space to hold the constant 1084202172485504433 * 10E-36 (yes I even made it 19 digits)
                          !mov dword ptr[ecx],   &hffffffef ;...-36 save in rndE. This is a binary image of the extended precision ...-36
                          !mov dword ptr[ecx+4], &hffffffff ;             "
                          !mov  word ptr[ecx+8], &h3fbf     ;             "
                          !fld tbyte [ecx]        ;float load ...-36
                          !fmulp st(1),st(0)      ;eq * ...-36 to range rnd to [0,1). This = eq / 9223372036854775816 but is much faster
                          !fld st(0)              ;copy final answer
                          !fstp rndE              ;save copy
                          !fstp FUNCTION          ;save FUNCTION
                      EXIT FUNCTION
                      gsRandomLong: 'here we generate a random LONG from 0 to &hffffffff
                      !mov eax, mwcSoph
                      !mov ecx, mwcRandom
                      !mul ecx
                      !Add eax, mwcCarry
                      !adc edx, 0
                      !mov mwcRandom, eax
                      !mov mwcCarry,  edx
                      RETURN 'done generating, mwcRandom holds final value
                      multiplier: 'a bunch of Sophie Germain primes. Each makes its own unique sequence of ~2^63 LONG random values
                      !DD 4055108289, 4183291290, 4066422669, 4010830218, 4144557798, 4047225099, 4169878863, 4156378278
                      !DD 4068734223, 4013003148, 4128794349, 4044603045, 4147482834, 4050081738, 4169007204, 4084483623
                      !DD 4182936234, 4061167764, 4116557445, 4184835774, 4098609075, 4000058700, 4005596580, 4131991143
                      !DD 4026365904, 4082490609, 4170263943, 4064971044, 4192040679, 4069686423, 4112450355, 4116008373
                      !DD 4051352658, 4131639393, 4026209880, 4143019908, 4057560153, 4153038378, 4178347353, 4101943515
                      !DD 4163817693, 4126770675, 4122227184, 4150506573, 4124871525, 4097114355, 4171215009, 4094254353
                      !DD 4185190458, 4184112513, 4187782989, 4037092584, 4114448259, 4096721880, 4003880118, 4035500259
                      !DD 4080989598, 4090215738, 4104202098, 4144153608, 4027213065, 4112123319, 4029634383, 4188620745
                      !DD 4003957254, 4158202674, 4165028370, 4101889029, 4064867064, 4056294705, 4117302630, 4094813610
                      !DD 4089078504, 4072584339, 4075250574, 4144182519, 4020827805, 4077052605, 4012941570, 4114015830
                      !DD 4015303260, 4012049835, 4031934513, 4123667379, 4025171265, 4149021864, 4020494469, 4152989853
                      !DD 4141465314, 4050172164, 4130534940, 4124347128, 4155032220, 4123523313, 4038610005, 4066391700
                      !DD 4052359893, 4138494750, 4046848368, 4015233183, 4065337650, 4181156010, 4149686553, 4115669703
                      !DD 4080411408, 4029985884, 4072279314, 4136476293, 4102312674, 4148638644, 4020161274, 4056852945
                      !DD 4084467288, 4090139205, 4152479904, 4129623354, 4189154793, 4042650633, 4113056934, 4070634510
                      !DD 4172190345, 4012616748, 4092782529, 4042027470, 4034320863, 4017110193, 4128178095, 4005317820
                      !DD 4121565819, 4160465475, 4093432608, 4094047308, 4092039654, 4132108680, 4160799915, 4109110719
                      !DD 4190254803, 4063105479, 4123739478, 4086096945, 4113466908, 4169157873, 4036670034, 4035486873
                      !DD 4154194098, 4074334704, 4006945965, 4119880785, 4050935955, 4131729105, 4170646809, 4191996963
                      !DD 4055775498, 4029162399, 4118132214, 4116397584, 4121266560, 4102454433, 4146555864, 4103353149
                      !DD 4119974010, 4080379233, 4192378968, 4061071950, 4104928533, 4042978743, 4188739878, 4066717740
                      !DD 4017709695, 4027617453, 4110604308, 4107339654, 4076278878, 4077074274, 4097495403, 4179562659
                      !DD 4187765853, 4187454249, 4015793904, 4083863454, 4078492929, 4166495943, 4101303048, 4149525330
                      !DD 4095286830, 4078227909, 4189944624, 4010811645, 4032304584, 4151394078, 4044317298, 4136517915
                      !DD 4198354635, 4192501860, 4073134869, 4060180830, 4076815050, 4190613315, 4142749785, 4122567564
                      !DD 4071542523, 4024430004, 4122798648, 4041267495, 4006243575, 4092566124, 4141397349, 4175565558
                      !DD 4159829190, 4173505479, 4084339563, 4085131608, 4081507743, 4069428324, 4011038568, 4092438129
                      !DD 4005482298, 4020895359, 4127615184, 4162803795, 4038272028, 4123171464, 4199942199, 4067713245
                      !DD 4129181838, 4021766328, 4141102845, 4002607668, 4051580310, 4082443044, 4078962945, 4072199883
                      !DD 4180693749, 4040763375, 4025696004, 4066226853, 4013137770, 4084688994, 4081465923, 4185884010
                      !DD 4184193840, 4095653625, 4071642489, 4003011123, 4021708860, 4038391383, 4003548888, 4016275635
                      !DD 4051483344, 4052001093, 4131504594, 4129105653, 4187278653, 4058921709, 4167113355, 4106971188
                      !DD 4074045393, 4069825200, 4009724565, 4120937589, 4119577560, 4151390115, 4000637598, 4088788530
                      !DD 4014859458, 4003633353, 4192075623, 4009856424, 4048255155, 4100175633, 4129717695, 4012882215
                      !DD 4119226824, 4122492603, 4074693864, 4062187338, 4022104890, 4186039455, 4191285474, 4165800789
                      !DD 4047934929, 4045886208, 4028478450, 4098395724, 4095869853, 4004229753, 4110500373, 4188458055
                      !DD 4093944063, 4122368673, 4136075109, 4024434645, 4145270010, 4121262090, 4051650480, 4076720613
                      !DD 4057135713, 4053301650, 4074379569, 4103950185, 4146078999, 4029125490, 4036104003, 4122595203
                      !DD 4173008610, 4155931704, 4048316175, 4178853645, 4049069715, 4187855514, 4193714559, 4132340133
                      !DD 4001184978, 4087342068, 4038996009, 4032782589, 4103313705, 4057212699, 4094324010, 4117022988
                      !DD 4016133978, 4057176333, 4081210119, 4183410330, 4054406019, 4008415374, 4131217578, 4049176725
                      !DD 4033804230, 4154677353, 4194818769, 4057689999, 4065887250, 4083913149, 4160269749, 4148719650
                      !DD 4086572148, 4079152770, 4198797849, 4025836533, 4121774838, 4114818903, 4193265369, 4005720123
                      !DD 4172736744, 4113446385, 4153872675, 4022863908, 4169665353, 4080875223, 4148976378, 4158173325
                      !DD 4012107315, 4146530883, 4042645638, 4189878099, 4075365840, 4053276279, 4112504730, 4144260888
                      !DD 4102144035, 4181673825, 4171915968, 4123257354, 4032551355, 4054454535, 4132616253, 4057321905
                      !DD 4174490559, 4165419468, 4169862234, 4116771594, 4009920498, 4164231630, 4163597154, 4181713095
                      !DD 4000268439, 4077171264, 4045424718, 4116626304, 4052701140, 4140380880, 4027965249, 4102323183
                      END FUNCTION
                      Last edited by John Gleason; 21 Oct 2008, 06:01 PM. Reason: uncommented GOSUB gsRandomLong, doh


                      • Those timings you posted sure show how sad my machine is. My numbers are six times slower approx.

                        You may find this interesting: I ran DO:Randomise: Rnd2long:LOOP and printed every 10,000,000 bytes, but I did a harsh test--I commented the GOSUB gsRandomLong so no extra mwc shuffle was done each loop. This tests mainly the soph and mwcRandom seed quality. Here's ENT & diehard. I was surprised, I didn't think it would do so well.
                        Entropy = 8.000000 bits per byte.
                        Optimum compression would reduce the size
                        of this 2030000812 byte file by 0 percent.
                        Chi square distribution for 2030000812 samples is 275.64, and randomly
                        would exceed this value 25.00 percent of the times.
                        Arithmetic mean value of data bytes is 127.4992 (127.5 = random).
                        Monte Carlo value for Pi is 3.141642009 (error 0.00 percent).
                        Serial correlation coefficient is -0.000020 (totally uncorrelated = 0.0).
                        this is diehard on the 1st 280MB of it:
                                        ***** TEST SUMMARY *****
                        All p-values:
                        Overall p-value after applying KStest on 258 p-values = 0.803316


                        • Okay, I'll toss in my opinion. All of the macros should begin with RND2. Every single macro, no matter what it is doing... RND2....

                          And I want to ask again about the values we are getting from RND2(). I've cranked through 2 to 3 billion of those RND2()'s with millions of seed values and have yet to see anything smaller than 2.03E-10 or larger than 0.99....906... with 10 leading 9s. Despite the simple statistics, I have a nagging suspicion that our algorithms will not yield extreme values near 0 nor near 1 in the tails. We're proposing that RND2 has a uniform distribution, not tapered tails. Based on MWC theory (not simple statistical theory), will we see two CONSECUTIVE mwcRandom values that are almost all binary 1s or all 0s ?? I ask this because we are building our extended precision dividend using two consecutive mwcRandom values.


                          • Yep, I like the additional macros.

                            Re the macro names Gary has come in with a very firm proposal. It is closer to yours John than mine. Maybe a PB poll is in order.

                            I'll need to chew on 'O:Randomise: Rnd2long:LOOP' and Gary's observation on 'tail avoidance'.


                            • The 'DO:Randomise: Rnd2long:LOOP' didn't surprise me.

                              GoSub gsRandomLong ' Shuffle mwcRandom a bit more and shuffle mwcCarry

                              is from the original Rnd2 which got a 'random' mwcSoph and a 'random' mwcRandom. No attempt was made to get a 'random' mwcCarry; the default mwcCarry was used. The above statement was used to provide a 'random' mwcCarry knowing it would be valid because the MWC algo would be used. mwcRndom would get shuffled further. [1]

                              Hence: Shuffle mwcRandom a bit more and shuffle mwcCarry

                              With the latest Rnd2(1) the current mwcCarry is used but it is checked to make sure that it is valid with the new multiplier.

                              The above statement then is not required.

                              [1] Added: See post #, wait for it........., 2.
                              Last edited by David Roberts; 21 Oct 2008, 06:56 PM.


                              • Gary, your naming scheme I think is the way to go. It seems like THE definitive answer. How can you confuse anything using it as you immediately know what it relates to. I change my vote to Gary's scheme.

                                Re. theory of MWC distribution of DWORD integers, using DWORD for simplicity of hex representation where &hffffffff is 4294967295: Let's look at all the nums in the sequence which is somewhat less than but let's say exactly 2^63 or &h7fffffffffffffff long. With every mwcRandom, call it r, that we multiply with the prime we get a carry, call it c.

                                Every r&c combination is absolutely unique in the sequence, and c will always be < the prime. In order for r&c uniqueness to exist over that whole range, every r from &h0 to &hffffffff inclusive must occur &h7fffffff times or you will get a duplicate.

                                Now my math skills are at their limit but... let's say our last r was 0. Can our next r be 0? I don't think so, because tho c can range from 0 to prime - 1, if it's 0 at the same time r is 0, our next generated random r*prime + c (or 0*prime+0) will = 0 and terminate the sequence.

                                Hence I now believe you are correct, that 0,0 cannot occur, 0,1 is the minimum, and on the other end &hffffffff, prime-2 is the maximum, or about &hfffffffffa000000. But let me think about this for a while to properly digest these numbas.


                                • I thought of something (ut oh, danger will robinson). Gary, if there is a skew in consecutive numbers, try generating one more dummy rnd in between. It won't slow it much. In noparams: add something like this:
                                    GOSUB gsRandomLong 'sub to generate LONG rnd value
                                    !lea ecx,   eq
                                    !mov [ecx], eax           ; = POKE LONG, eqPtr,     rndL
                                    GOSUB gsRandomLong 'sub to generate LONG rnd value
                                    GOSUB gsRandomLong 'sub to generate LONG rnd value
                                    !lea ecx,     eq
                                    !mov [ecx+4], eax         ; = POKE LONG, eqPtr + 4, rndL
                                  seed it once and let 'er run. Check if you get higher lower limits or not. You could try maybe even three gosubs.


                                  • Further reading has got me to:

                                    There are two cases where mwc will have a period of one.
                                    1. Where both mwcRandom and mwcCarry are zeo.
                                    2. Where mwcRandom = 2^32-1 and mwcCarry = mwcSoph - 1

                                    I have used the above as a default sequence and found the statement [2] to be true but ONLY IF mwcRandom is a Dword with mwcCarry as either a Dword or Long. If mwcRandom is Long with mwcCarry as either Dword or Long then [2] is false.

                                    [1] is true for any combination of Dword/Long for mwcRandom and mwCarry.

                                    I wonder if the tail problem is related to mwcRandom being a Long.

                                    Gary has questioned this and I considered it when I wrote the original Rnd2 but convinced myself that it made no odds. Maybe it does?

                                    BTW, mwcCarry can be >= mwcSoph


                                    • When loading the quad on our way to a RND2() value, remember that our msbit is treated as a sign bit. We load the quad as a two's complement signed integer using FILD. For positive only values, we mask out the msbit.

                                      I'm puzzled about the reported divisor in our noParams section. Doesn't 2^63 = 9223372036854775808, not 9223372036854775816 ?

                                      FYI, I took 863 crypto seeds and processed 100 million RND2()'s per seed. That is 86.3 billion values. (I gave up after the 863'd loop.)
                                      The smallest value was 1.16986725211612463e-11.
                                      The largest value was 0.999999999989856264 .

                                      We're using a single dword to calculate a two parameter integer value. Can't we just use a single dword to calculate the noParam value?
                                      Last edited by Gary Ramey; 21 Oct 2008, 09:57 PM.


                                      • Back on the topic for calculating RND2() noParams results... The following code might help you see what is happening as we try to accurately represent the "divisor" to scale our result into the 0-1 range.

                                        'Evaluation of representing divisor in RND2() noParams calculations 
                                        #DIM ALL
                                        FUNCTION PBMAIN() AS LONG
                                        STATIC rndE AS EXT
                                        STATIC rndQE AS EXT
                                        LOCAL str1, str2, rndEstr AS STRING
                                        STATIC rndEbdw, rndEudw AS DWORD
                                        STATIC rndEexp AS WORD
                                        rndE = 1.   'Just initializing. 
                                        ' Next load rndE with a value using code from Rnd2TC.
                                        #REGISTER NONE
                                        !lea ecx, rndE          ;space TO hold the constant 1084202172485504433 * 10E-36 (yes I even made it 19 digits)
                                        !mov DWORD PTR[ecx],   &hffffffef ;...-36 SAVE IN rndE. This IS a BINARY IMAGE OF the EXTENDED precision ...-36
                                        !mov DWORD PTR[ecx+4], &hffffffff ;       "      "
                                        !mov  WORD PTR[ecx+8], &h3fbf     ;       "      "
                                        !fld tbyte [ecx]        ;float LOAD ...-36
                                        ''!fmulp st(1),st(0)      ;eq * ...-36 TO RANGE RND TO [0,1). This = eq / 9223372036854775816 but IS much faster
                                        ''!fld st(0)              ;COPY final answer
                                        !fstp rndE              ;SAVE COPY
                                        ' Now create the hex representation of this rndE
                                        rndEstr = MKE$(rndE)
                                        rndEexp = CVWRD(rndEstr,9)
                                        rndEudw = CVDWD(rndEstr,5)
                                        rndEbdw = CVDWD(rndEstr)
                                        str1 = "RND2TC  = " & HEX$(rndEexp,4) & " " & HEX$(rndEudw,8) & " " & HEX$(rndEbdw,8)
                                        ' Now experiment with different values of rndQE and look at the result.
                                        ' Once you get above ....75810 or so, then all the results are the same.
                                        ' It does not appear possible to precisely load 1./ 2^63-1 as a constant.
                                        ' This test program is using rndQE as an extended fp value to avoid issues
                                        '    with the representation of 2^63 in a signed quad.
                                        rndQE = 2^63 -1.   ' try +8.   'Note: 2^63 = 9223372036854775808.
                                                           ' The RND2TC ...816 value is closest to 2^63+8 or +9.     
                                        rndE =  1./rndQE   ' 1.0842021724855044341250022359522e-19 = 1./ 2^63 -1
                                        'Create the hex representation of this rndE value
                                        rndEstr = MKE$(rndE)
                                        rndEexp = CVWRD(rndEstr,9)
                                        rndEudw = CVDWD(rndEstr,5)
                                        rndEbdw = CVDWD(rndEstr)
                                        str2 = "Testing = " & HEX$(rndEexp,4) & " " & HEX$(rndEudw,8) & " " & HEX$(rndEbdw,8)
                                        ' And finally display our results
                                        ? "rndQ = " + STR$(rndQE, 18) +$CR+$LF+_
                                          "rndE = " + STR$(rndE, 18) +$CR+$LF+    str1 +$CR+$LF+ str2
                                        '' Note:  1./(2^63 -1), after FPU extended calcs, has hex pattern of 3FC0 8000 0000 0000 0001 .
                                        END FUNCTION


                                        • Perhaps you want to experiment with the post 135 noParams calculations using specific mwcRandom results. Some unanticipated things are happening in interaction with togScope.

                                          John's original multiplier will produce an "all nines" maximum answer if togScope = false and both mwcRandoms are __unsigned__ FFFF FFFF.

                                          Here is the code to experiment with:

                                          'Program to manually calculate RND2() result for a chosen input
                                          'This is using the noParams code from post 135
                                          #DIM ALL
                                          FUNCTION PBMAIN() AS LONG
                                          STATIC rndE AS EXT
                                          STATIC eq AS QUAD
                                          STATIC togScope AS LONG
                                          LOCAL str1, rndEstr AS STRING
                                          STATIC rndEbdw, rndEudw AS DWORD
                                          STATIC rndEexp AS WORD
                                          rndE = 1.   'Just initializing. 
                                          ' Manually select an eq value and process with noParams code from Rnd2TC.
                                          #REGISTER NONE
                                          togScope = 1       'Take a look at what happens when togScope =true and eq =&h0ffffffff 
                                          eq = &h0ffffffff   'forced to be unsigned value
                                          !lea ecx,   eq
                                            !mov [ecx], eax
                                          !lea ecx,     eq
                                            !mov [ecx+4], eax
                                          !cmp togScope, 0        ;DO we need TO make it 50/50 + AND - ?
                                            !jne short rndNeg       ;jump IF we DO.
                                            !lea ecx, eq            ;eq PTR
                                            !AND DWORD PTR[ecx+4], &h7fffffff ;ONLY positive [0,1) IS needed, so make eq +
                                              !fild QWORD eq          ;float LOAD eq which IS now a complete QUAD RANDOM
                                          !lea ecx, rndE  
                                          !mov DWORD PTR[ecx],   &hffffffef 
                                          !mov DWORD PTR[ecx+4], &hffffffff 
                                          !mov  WORD PTR[ecx+8], &h3fbf     
                                          !fld tbyte [ecx] 
                                          !fmulp st(1),st(0)
                                          ''!fld st(0)   ;COPY final answer
                                          !fstp rndE 
                                          ' Now create the hex representation of this rndE
                                          rndEstr = MKE$(rndE)
                                          rndEexp = CVWRD(rndEstr,9)
                                          rndEudw = CVDWD(rndEstr,5)
                                          rndEbdw = CVDWD(rndEstr)
                                          str1 = "RND2TC  = " & HEX$(rndEexp,4) & " " & HEX$(rndEudw,8) & " " & HEX$(rndEbdw,8)
                                          ? "rndE = " + STR$(rndE, 18) +$CR+$LF+   str1
                                          END FUNCTION