Rand and Randomize Information - SMcNeill - 04-25-2022
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:
Quote
Quote:;***
; 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 BXX
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 [i]truly[/i] random -- which is why we call it pseduo-random, at best.
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.
|