Obsah / Utility / RANDOM / Náhodné číslo v plném rozsahu
Zdrojový kód:
INCLUDE\UTIL\RANDOM.INC, UTIL\RANDOM.ASM
Náhodné
číslo v plném rozsahu
; -----------------------------------------------------------------------------
; Get random byte
; -----------------------------------------------------------------------------
; OUTPUT: AL = random byte (0 to 255)
; -----------------------------------------------------------------------------
RandByte: push edx ; push EDX
push eax ; push EAX
RANDNEXT ; next global random seed
shr eax,16 ; EAX <- random word
xchg eax,edx ; EDX <- push random word
pop eax ; pop EAX
mov al,dl ; AL <- random byte
pop edx ; pop EDX
ret
|
Funkce RandByte
poskytne v registru AL náhodné číslo v rozsahu 0 až 255.
Nejdříve je pomocí makra RANDNEXT vypočtena nová hodnota
generátoru náhody. Z výsledku EAX:EDX je převzat bajt od 16.
bitu registru EAX, který má rovnoměrnější rozdělení
náhodnosti než nižší bajty. Výsledek je uložen do registru
AL, ostatní registry (včetně zbytku registru EAX) nejsou
ovlivněny.
; -----------------------------------------------------------------------------
; Get random word
; -----------------------------------------------------------------------------
; OUTPUT: AX = random word (0 to 65535)
; -----------------------------------------------------------------------------
RandWord: push edx ; push EDX
push eax ; push EAX
RANDNEXT ; next global random seed
shr eax,16 ; EAX <- random word
xchg eax,edx ; EDX <- push random word
pop eax ; pop EAX
xchg ax,dx ; AX <- random word
pop edx ; pop EDX
ret
|
Funkce RandWord
poskytne v registru AX náhodné číslo v rozsahu 0 až 65535.
Nejdříve je pomocí makra RANDNEXT vypočtena nová hodnota
generátoru náhody. Z výsledku EAX:EDX je převzato slovo od
16. bitu registru EAX, které má rovnoměrnější rozdělení
náhodnosti než nižší slovo. Výsledek je uložen do registru
AX, ostatní registry (včetně zbytku registru EAX) nejsou
ovlivněny.
; -----------------------------------------------------------------------------
; Get random quadruple word
; -----------------------------------------------------------------------------
; OUTPUT: EDX:EAX = random quadruple word (0 to 0ffffffffffffffffh)
; NOTES: RANDNEXT result is not used to avoid number predictions.
; -----------------------------------------------------------------------------
RandQWord: push edx ; push EDX
RANDNEXT ; next global random seed
pop edx ; pop EDX
xchg eax,edx ; EDX <- first random double word
; RandDWord must follow!
; -----------------------------------------------------------------------------
; Get random double word
; -----------------------------------------------------------------------------
; OUTPUT: EAX = random double word (0 to 0ffffffffh)
; -----------------------------------------------------------------------------
RandDWord: push edx ; push EDX
RANDNEXT ; next global random seed
pop edx ; pop EDX
ret
|
Funkce RandDWord
poskytne v registru EAX náhodné číslo v rozsahu 0 až
0ffffffffh. Nejdříve je pomocí makra RANDNEXT vypočtena nová
hodnota generátoru náhody. Z výsledku EAX:EDX je převzata
pouze hodnota registru EAX. Hodnota registru EDX je uchována
instrukcemi PUSH/POP.
Funkce RandQWord
poskytne v dvojici registrů EDX:EAX náhodné číslo v rozsahu
0 až 0ffffffffffffffffh. Nejdříve vypočte pomocí makra
RANDNEXT první dvojslovo, výsledek uloží do registru EDX a
poté pokračuje funkcí RandDWord, která vypočte náhodné
číslo pro registr EAX.
Pro výpočet
RandQWord není použit celý výsledek výpočtu generátoru
náhody EAX:EDX z důvodu, aby se uživateli nezpřístupnila
celá hodnota generátoru náhody a nebylo tak možné dopředu
předpovídat náhodná čísla.
; -----------------------------------------------------------------------------
; Get random float, precision 7 decimal digits
; -----------------------------------------------------------------------------
; OUTPUT: EAX = random float (0 to 1, including "0", excluding "1")
; NOTES: Number in EAX is single-precision floating-point IEE 754.
; This function does not use FPU.
; To load result into FPU, you can use this sequence:
; push eax ; push result into stack
; fld dword [esp] ; load result into FPU
; pop eax ; pop result from the stack
; Possible values (only 7 decimal digits are significant):
; 0.0000000
; 0.0000001
; 0.0000002
; ...
; 0.4999999
; 0.5000000
; 0.5000001
; ...
; 0.9999999
; -----------------------------------------------------------------------------
; ------------- Push registers
RandFloat: push ecx ; push ECX
; ------------- Generate random double word (-> EAX)
push edx ; push EDX
RANDNEXT ; next global random seed (-> EAX)
pop edx ; pop EDX
and eax,~1ffh ; clear unused bits
; ------------- Find highest bit "1" (-> ECX)
bsr ecx,eax ; ECX <- find highest bit "1"
jz RandFloat2 ; zero number
; ------------- Prepare mantissa (-> EAX)
ror eax,cl ; EAX <- rotate highest bit "1" out
shr eax,9 ; EAX <- shift mantissa to its position
; ------------- Prepare exponent (-> EDX)
mov ch,127-32 ; CH <- exponent for "1.0000" - 32
add ch,cl ; CH <- exponent
movzx ecx,ch ; ECX <- exponent
shl ecx,23 ; ECX <- rotate exponent to position
; ------------- Pop registers
or eax,ecx ; EAX <- add exponent to result
RandFloat2: pop ecx ; pop ECX
ret
|
Funkce RandFloat
vypočte náhodné desetinné číslo v rozsahu 0 (včetně) až
1 (vyjma) s přesností 7 desetinných míst, výsledek navrátí
v registru EAX ve formátu IEE 754 single-precision. Tato funkce
nepoužívá matematický koprocesor (FPU) a proto může být
volána z jádra systému bez nutnosti uchovávat obsah
koprocesoru. K načtení výsledku do registru koprocesoru lze
použít posloupnost instrukcí: "push eax", "fld
dword ptr [esp]", "pop eax".
Funkce vygeneruje
náhodné číslo v rozsahu dvojslovo a vynuluje nejnižších 9
bitů čísla, protože využívá pouze 23 bitů čísla. Bez
vynulování těchto nejnižších 9 bitů by se zvýšila
četnost velmi malých čísel z důvodu zaokrouhlení nahoru.
Instrukce BSR
vyhledá nejvyšší bit "1" v čísle a jeho bitovou
pozici uloží do registru ECX. Je-li náhodné číslo rovno
nule, funkce se předčasně ukončí s navrácením nulové
hodnoty.
Formát IEE 754
single-precision obsahuje v nižších 23 bitech normalizovanou
mantisu čísla, přičemž nejvyšší bit "1" mantisy
je vypuštěn. Funkce připraví mantisu čísla tak, že
nejdříve rotuje náhodné číslo v registru EAX doprava tak,
aby se nejvyšší bit "1" dostal na bitovou pozici 0.
K tomu využije registr CL obsahující index pozice bitu v
číslu. Po provedení rotace posune číslo o 9 bitů doprava,
tím posune mantisu na správnou bitovou pozici, tj. bity 0 až
22.
Exponent formátu IEE
754 zabírá 8 bitů od bitu 23 po bit 30 s offsetem 127. Hodnota
exponentu se vypočte jako "127 - 32 + počet provedených
rotací mantisy doleva". Tím se číslo převede do rozsahu
0 až 1. Vypočtený exponent se posune na správnou bitovou
pozici rotací exponentu o 23 bitů doleva.
Na závěr se
sestaví výsledná hodnota čísla sloučením mantisy s
exponentem. Vygenerované číslo má hodnoty z řady 0.0000000,
0.0000001, 0.0000002, ... 0.4999999, 0.5000000, 0.5000001, ...
0.9999999.
; -----------------------------------------------------------------------------
; Get random number using FPU, precision 10 decimal digits
; -----------------------------------------------------------------------------
; OUTPUT: ST0 = random float (0 to 1, including "0", excluding "1")
; NOTES: Warning - this function uses FPU (push/pop state if in kernel).
; Possible values (only 10 decimal digits are significant):
; 0.0000000000
; 0.0000000002
; 0.0000000005
; ...
; 0.4999999998
; 0.5000000000
; 0.5000000002
; ...
; 0.9999999998
; -----------------------------------------------------------------------------
; ------------- Push registers
RandFloatFPU: push eax ; push EAX
; ------------- Prepare HIGH dword of the number (= 0)
xor eax,eax ; EAX <- 0
push eax ; store HIGH dword of the number
; ------------- Generate LOW dword of the number
push edx ; push EDX
RANDNEXT ; next global random seed (-> EAX)
pop edx ; pop EDX
push eax ; store number to the stack
; ------------- Recalc number to range 0 to 1
fwait ; synchronize FPU
fild qword [esp] ; ST0 <- random number
fmul qword [RandFltCoef] ; recalc to range 0 to 1
pop eax ; destroy number from the stack
pop eax ; destroy HIGH dword from the stack
; ------------- Pop registers
pop eax ; pop EAX
ret
; ------------- Float coefficient 1/100000000h (= 3df0000000000000h)
RandFltCoef: dq 0.00000000023283064365386962890625
|
Funkce RandFloatFPU
vypočte náhodné desetinné číslo v rozsahu 0 (včetně) až
1 (vyjma) s přesností 10 desetinných míst, výsledek
navrátí v registru ST0 matematického koprocesoru (FPU).
Protože funkce používá matematický koprocesor, je nutné
pamatovat na uchování stavu koprocesoru v případě, že
funkce je interně používána jádrem systému.
Funkce vygeneruje
náhodné číslo v rozsahu dvojslovo a načte ho do registru ST0
koprocesoru. Instrukce FILD čte celé číslo se znaménkem,
proto je náhodné číslo rozšířeno na QWORD doplněním
vyššího dvojslova s hodnotou nula.
Vygenerované
náhodné číslo je dále přepočítáno do rozsahu 0 až 1
tak, že je vyděleno hodnotou 100000000h, což je maximální
rozsah generovaného náhodného čísla. Pro zvýšení
efektivnosti je namísto dělení použito násobení
převrácenou hodnotou čísla - konstanta RandFltCoef.
Vygenerované číslo
má hodnoty z řady 0.0000000000, 0.0000000002, 0.0000000005, ...
0.4999999998, 0.5000000000, 0.5000000002, ... 0.9999999998.
; -----------------------------------------------------------------------------
; Get random double, precision 16 decimal digits
; -----------------------------------------------------------------------------
; OUTPUT: EDX:EAX = random double (0 to 1, including "0", excluding "1")
; NOTES: Number in EDX:EAX is double-precision floating-point IEE 754.
; This function does not use FPU.
; To load result into FPU, you can use this sequence:
; push edx ; push result HIGH into stack
; push eax ; push result LOW into stack
; fld qword [esp] ; load result into FPU
; pop eax ; pop result LOW from the stack
; pop edx ; pop result HIGH from stack
; Possible values (only 16 decimal digits are significant):
; 0.0000000000000000
; 0.0000000000000001
; 0.0000000000000002
; ...
; 0.4999999999999999
; 0.5000000000000000
; 0.5000000000000001
; ...
; 0.9999999999999999
; -----------------------------------------------------------------------------
; ------------- Push registers
RandDouble: push ebx ; push EBX
push ecx ; push ECX
xor ecx,ecx ; ECX <- 0
; ------------- Generate random quadruple word (-> EDX:EAX)
call RandQWord ; generate random quadruple word
and edx,1fffffh ; mask mantissa HIGH (+ high "1" bit)
jz RandDouble4 ; number is only DWORD
; ------------- Find highest bit "1" (-> EBX)
bsr ebx,edx ; EBX <- find highest bit "1"
; ------------- Prepare mantissa (-> EDX:EAX)
mov cl,20 ; ECX <- maximal possible bit position
sub cl,bl ; ECX <- number of bits to rotate
shld edx,eax,cl ; EDX <- shift mantissa HIGH
shl eax,cl ; EAX <- shift mantissa LOW
; ------------- Prepare exponent (-> EDX)
mov bx,1022 ; EBX <- exponent for "1.0000" - 1
jmp short RandDouble6
; ------------- Find highest bit "1" (-> EBX)
RandDouble4: bsr ebx,eax ; EBX <- find highest bit "1"
jz RandDouble8 ; zero number
; ------------- Prepare mantissa (-> EDX:EAX)
mov cl,31 ; ECX <- maximal possible bit position
sub cl,bl ; ECX <- number of bits to rotate
shl eax,cl ; EAX <- shift mantissa
shld edx,eax,21 ; EDX <- mantissa HIGH
shl eax,21 ; EAX <- mantissa LOW
; ------------- Prepare exponent (-> EDX)
mov bx,1022-21 ; EBX <- exponent for "1.0000" - 1 - 21
; ------------- Composite result
RandDouble6: sub ebx,ecx ; EBX <- new exponent
shl ebx,52-32 ; EBX <- rotate exponent to position
and edx,0fffffh ; mask mantissa HIGH
or edx,ebx ; add exponent to result
; ------------- Pop registers
RandDouble8: pop ecx ; pop ECX
pop ebx ; pop EBX
ret
|
Funkce RandDouble
vypočte náhodné desetinné číslo v rozsahu 0 (včetně) až
1 (vyjma) s přesností 16 desetinných míst, výsledek
navrátí v registrech EDX:EAX ve formátu IEE 754
double-precision. Tato funkce nepoužívá matematický
koprocesor (FPU) a proto může být volána z jádra systému
bez nutnosti uchovávat obsah koprocesoru. K načtení výsledku
do registru koprocesoru lze použít posloupnost instrukcí:
"push edx", "push eax", "fld qword ptr
[esp]", "pop eax", "pop edx".
Funkce vygeneruje
náhodné číslo v rozsahu čtyřslovo. Vynuluje nejvyšších
11 bitů čísla, protože využívá pouze 53 bitů čísla.
Je-li vyšší dvojslovo čísla (tj. registr EDX) nula,
pokračuje se částí pro náhodné číslo v rozsahu DWORD.
Má-li číslo rozsah
čtyřslova, vyhledá se ve vyšším dvojslovu (registr EDX)
pomocí instrukce BSR pozice nejvyššího bitu "1" a
uloží se do registru EBX.
Připraví se mantisa
čísla - vypočte se počet posuvů, které je potřeba udělat,
aby se nejvyšší bit čísla dostal na bitovou pozici 20 (tj.
posun = 20 - pozice nejvyššího bitu) a provede se posun
mantisy doleva o zjištěný počet posuvů. Posuvy se dělají
přes nižší i vyšší dvojslovo čísla, proto se využívá
instrukce SHLD, která zajistí vícebitový přenos mezi
registry. Mantisa bude na bitové pozici 0 až 52 čtyřslova
EDX:EAX, nejvyšší bit 52 se později odstraní (je automaticky
daný formátem čísla).
Do registru EBX se
připraví výchozí střední hodnota exponentu, od které se
později odečte bitový posun mantisy.
Druhá větev funkce
pokračuje v případě, že vygenerované náhodné číslo má
rozsah pouze dvojslovo (vyšší dvojslovo je 0). Pomocí
instrukce BSR se vyhledá pozice nejvyššího bitu "1"
v nižším dvojslově čísla (tj. v registru EAX) a uloží se
do registru EBX. V případě nulové hodnoty náhodného čísla
se funkce ukončí ihned, výsledkem generátoru je hodnota 0.
Připraví se mantisa
čísla - vypočte se počet posuvů, které je potřeba udělat,
aby se nejvyšší bit čísla dostal na bitovou pozici 31 a
provede se posun obsahu nižšího dvojslova doleva o vypočtený
počet bitů. Poté se mantisa převede na normalizovaný tvar
posunem o 21 bitů doleva přes oba registry vygenerovaného
čísla. Mantisa bude na bitové pozici 0 až 52 čtyřslova
EDX:EAX, nejvyšší bit 52 se později odstraní.
Do registru EBX se
připraví výchozí střední hodnota exponentu, od které se
později odečte bitový posun mantisy, snížená o počet
bitů, o které se mantisa musela posunout v tomto případě,
kdy bylo náhodným číslem pouze jedno dvojslovo.
Pokračuje společná
část pro obě větve, která zajistí sloučení výsledku. Od
výchozí střední hodnoty exponentu v registru EBX je odečten
provedený posun mantisy (který je uložen v registru ECX) a
exponent je posunut na správnou bitovou pozici 52.
Zamaskováním registru EDX se vynuluje nejvyšší bit
"1" mantisy, který je automaticky daný formátem
čísla a mantisa s exponentem se sloučí.
Vygenerované číslo
má hodnoty z řady 0.0000000000000000, 0.0000000000000001,
0.0000000000000002, ... 0.9999999999999999, 0.5000000000000000,
0.5000000000000001, ... 0.9999999999999999.
; -----------------------------------------------------------------------------
; Get random number using FPU, precision 16 decimal digits
; -----------------------------------------------------------------------------
; OUTPUT: ST0 = random double (0 to 1)
; NOTES: Warning - this function uses FPU (push/pop state if in kernel).
; Possible values (only 16 decimal digits are significant):
; 0.0000000000000000
; 0.0000000000000001
; 0.0000000000000002
; ...
; 0.4999999999999999
; 0.5000000000000000
; 0.5000000000000001
; ...
; 0.9999999999999999
; -----------------------------------------------------------------------------
; ------------- Push registers
RandDoubleFPU: push eax ; push EAX
push edx ; push EDX
; ------------- Generate HIGH random number
RANDNEXT ; next global random seed (-> EAX)
and eax,1fffffh ; reset unused bits
push eax ; store HIGH number to the stack
; ------------- Generate LOW random number
RANDNEXT ; next global random seed (-> EAX)
push eax ; store LOW number to the stack
; ------------- Recalc number to range 0 to 1
fwait ; synchronize FPU
fild qword [esp] ; ST0 <- random number
fmul qword [RandDblCoef] ; recalc to range 0 to 1
pop eax ; destroy LOW number from the stack
pop eax ; destroy HIGH number from the stack
; ------------- Pop registers
pop edx ; pop EDX
pop eax ; pop EAX
ret
; ------------- Double coefficient 1/20000000000000h (= 3ca0000000000000h)
RandDblCoef: dq 1.1102230246251565404236316680908e-16
|
Funkce RandDoubleFPU
vypočte náhodné desetinné číslo v rozsahu 0 až 1 s
přesností 16 desetinných míst, výsledek navrátí v registru
ST0 matematického koprocesoru (FPU). Protože funkce používá
matematický koprocesor, je nutné pamatovat na uchování stavu
koprocesoru v případě, že funkce je interně používána
jádrem systému.
Funkce vygeneruje
náhodné číslo v rozsahu čtyřslovo a načte ho do registru
ST0 koprocesoru. Vynuluje nejvyšších 11 bitů čísla,
protože nebudou využity.
Vygenerované
náhodné číslo je dále přepočítáno do rozsahu 0 až 1
tak, že je vyděleno hodnotou 20000000000000h, což je
maximální rozsah generovaného náhodného čísla. Pro
zvýšení efektivnosti je namísto dělení použito násobení
převrácenou hodnotou čísla - konstanta RandDblCoef.
Vygenerované číslo
má hodnoty z řady 0.0000000000000000, 0.0000000000000001,
0.0000000000000002, ... 0.9999999999999999, 0.5000000000000000,
0.5000000000000001, ... 0.9999999999999999.
Obsah / Utility / RANDOM / Náhodné číslo v plném rozsahu