QB64 Phoenix Edition
RND and RANDOMIZE information - Printable Version

+- QB64 Phoenix Edition (https://qb64phoenix.com/forum)
+-- Forum: Official Links (https://qb64phoenix.com/forum/forumdisplay.php?fid=16)
+--- Forum: Learning Resources and Archives (https://qb64phoenix.com/forum/forumdisplay.php?fid=13)
+--- Thread: RND and RANDOMIZE information (/showthread.php?tid=3644)



RND and RANDOMIZE information - SMcNeill - 04-28-2025

I'd posted this information back at the old forums back in 2019 and I'd thought that I'd copied it over here for everyone, but I couldn't find it.  Here is everything you ever wanted to know about RND and RANDOMIZE. 

First, a link to the original I posted: https://qb64forum.alephc.xyz/index.php?topic=1414.msg105988#msg105988

Then a quick copy of the info in case that site ever goes down for whatever reason:



For folks who want a little extra information about how RND and RANDOMIZE work in QBASIC (and has been imitated to work the same in QB64), here's a little old documentation I dug up from the old drive on them:

Code: (Select All)
;***
; RANDOM - RANDOM number generator AND RANDOMIZE
;
;        Copyright <C> 1986, Microsoft Corporation

;
; Algorithm:
;
; We use the "linear congruential" method FOR RANDOM numnber generation. The
; formula IS:
;
;        x1 = (x0 * a + c) MOD 2^24
;
; where
;
;        x1 = IS a new RANDOM number in the range [0..1^24]
;        x0 = the previous RANDOM number (OR the seed, FOR the first one)
;        a  = 214,013
;        c  = 2,531,011
;
; The RND FUNCTION returns a floating POINT number:
;
;        x1 / (2^24)
;
; which changes the range TO [0..1].

;***
;GetNextRnd -- GET NEXT RANDOM number
;MakeFloat -- make the number in [b$RndVar] into a R4
;
;Purpose:
;        GET NEXT RANDOM number in sequence.
;Entry:
;        [b$RndVar] has the seed.
;EXIT:
;        [AX]        = *B$AC which contains the R4 result
;Exceptions:
;        none
;*******************************************************************************

cProc        GetNextRnd,<NEAR>     

cBegin                             
        PUSH        DI             
        MOV        AX,[WORD PTR b$RndVar]        ;low half of previous number
        MOV        CX,[RndA]        ;low half of A
        MUL        CX
        XCHG        AX,DI                ;save low half in DI
        MOV        BX,DX                ;  high half in BX
        MOV        AX,[WORD PTR b$RndVar+2] ;high half of previous
        MUL        CX
        ADD        BX,AX                ;sum partial products
        MOV        AX,[RndA]
        MUL        [WORD PTR b$RndVar]       
        ADD        BX,AX                ;last partial product (since we're mod 2^24)
        ADD        DI,[RndC]        ;add in constant C
        ADC        BL,BYTE PTR [RndC]
        XOR        BH,BH                ;extended 24-bit number TO 32 bits FOR NORM
        MOV        DX,DI                ;number in BX:DX
        MOV        [WORD PTR b$RndVar],DX        ;save FOR NEXT time
        MOV        [WORD PTR b$RndVar+2],BX
        POP        DI             
MakeFloat:                     

        FILD        b$RndVar        ; PUT 24-bit INTEGER ON numeric stack
        FDIV        FP_2T24        ; ST0 = seed/2^24
        MOV        BX,OFFSET DGROUP:B$AC
        FSTP        DWORD PTR [BX]        ; PUT s.p. equivalent into FAC
        XCHG        AX,BX                ; result IS *R4 in AX
        FWAIT                        ; ensure result in RAM prior TO RETURN

cEnd                                ; EXIT TO caller

;***[6]
;B$RNZP - RANDOMIZE statement
;void B$RNZP (R8 SeedNum)
;
;Purpose:
;        The number IS set into the middle word of the current RANDOM
;        number AS the seed FOR the NEXT one.
;Entry:
;        R8 SeedNum
;EXIT:
;        A new seed IS created in RndVar, based ON the seed value at entry
;        AND the least significant 2-words of the INPUT parameter.
;Exceptions:
;        none
;*******************************************************************************

cProc        B$RNZP,<PUBLIC,FAR>     
        ParmQ        SeedNum        ; R8 seed number
cBegin                             
        LEA        BX,SeedNum+4        ; GET MOST significant digits
        MOV        AX,[BX]        ; GET word of D.P. number
        XOR        AX,[BX+2]        ; XOR with the NEXT word
        MOV        [WORD PTR b$RndVar+1],AX ; replace middle word of current s.p. seed
                                ;        with this value - - now we're reseeded.
cEnd                                ; EXIT

As you can see, we don't have any true randomness with RND in QB64.  In fact, our results are calculated on a mathematical formula!  (Which is why we always get the same results if we don't use RANDOMIZE TIMER to jump to some off point in the list of numbers we generate and use.)

If you're interested in this stuff, then here it is.  If not, then just ignore this topic and trust that RND isn't truly random -- which is why we call it pseduo-random, at best.  Wink

And, after a little more digging, I discovered this is the truth for QB64's randomize:

Apparently either the documentation I found is old and didn't apply to QBASIC RND (maybe it was the formula used with some other version Microsoft produced?), or else QB64 uses a different RND formula.

What we actually use is this one (as taken from libqb.cpp):

Code: (Select All)
float func_rnd(float n,int32 passed){
    if (new_error) return 0;
   
    static uint32 m;
    if (!passed) n=1.0f;
    if (n!=0.0){
        if (n<0.0){
            m=*((uint32*)&n);
            rnd_seed=(m&0xFFFFFF)+((m&0xFF000000)>>24);
        }
        rnd_seed=(rnd_seed*16598013+12820163)&0xFFFFFF;
    }     
    return (double)rnd_seed/0x1000000;
}

Instead of a formula where Seed = (Seed * 214013 + 2531011) MOD 2 ^ 24, we use one where rnd_seed=(rnd_seed*16598013+12820163)&0xFFFFFF;

Basically the concept is the same, but the formula for the calculations are different in the two versions.

I wonder how QB64's formula compares against QB45's. If anyone has a version of QB45 they can run, can you kindly tell me what the output might be for the following:

Code: (Select All)
FOR i = 1 TO 20
    PRINT RND, Rand
NEXT

FUNCTION Rand
    STATIC Seed
    x1 = (Seed * 214013 + 2531011) MOD 2 ^ 24
    Seed = x1
    Rand = x1 / 2 ^ 24
END FUNCTION



RE: RND and RANDOMIZE information - tantalus - 04-28-2025

(04-28-2025, 08:29 AM)SMcNeill Wrote: I wonder how QB64's formula compares against QB45's. If anyone has a version of QB45 they can run, can you kindly tell me what the output might be for the following:

Code: (Select All)
FOR i = 1 TO 20
    PRINT RND, Rand
NEXT

FUNCTION Rand
    STATIC Seed
    x1 = (Seed * 214013 + 2531011) MOD 2 ^ 24
    Seed = x1
    Rand = x1 / 2 ^ 24
END FUNCTION



[Image: Screenshot-2025-04-28-11-34-08.png]