![]() |
Aus Bytefolge (zufällig) eine Realzahl bilden
Ich baue mir gerade ein Klasse, die mir mit Hilfe von CryptGenRandom (AdvAPI32.DLL) eine Zufallszahl liefern soll.
Für ganze Zahlen kein Problem. CryptGenRandom liefert beliebig lange Bytefolgen, die ich dann einfach in den Speicherbereich der jeweiligen Ganzzahl (Byte/Integer/Cardinal/Int64) schiebe. Bei reelen Zahlen (Single/Double) bin ich aber nicht zufrieden. Die sind ja nicht ganz so statisch aufgebaut, wie ein Integer-Wert (Vorzeichen, Exponent, Mantisse). Folgende Versuche habe ich hinter mir: 1) Schiebe ich nun einfach 8 Byte in den Speicherbereich eines Double, kommen zwar zufällige Zahlen dabei raus, aber 70% liegt bei 0.0, Rest extrem hoch (liegt wohl am theoretischen Wertebereich). Zudem scheine ich beim Single-Typ auch ab und zu eine ungültige Bit-Belegung zu produzieren. 2) Ich nehme eine zufällige Int64-Zahl (geht ja problemlos) und setze die Kommastelle zufällig. Das Ergebnis ist ganz OK, allerdings recht umständlich, die Dezimalstellen zu ermitteln und ich brauche Delphi-Random zum setzen des Komma (Mein Zufallsgenerator kann ja erst nach ermitteln einer Fließkommazahl (für %-Wert) eine Range-Methode liefern) 3) Ich ermittel zwei ganze Zahlen... Int64 als Vorkomma- und Cardinal als Nachkommawert. Sind aber beide Werte hoch, komme ich deutlich über die sinnvollen 16 Stellen für ein Double. 4) Ich nehme einfach ein Festkomma (Currency) in das ich die Bytes schiebe und weise es danach einem Double zu. Ist zumindest schnell und der Wertebereich ist ganz OK. Meine derzeit verwendete Methode. *** Wie kann ich am besten auf Basis einer zufälligen Bytefolge eine Single/Double Zufallszahl bilden? |
AW: Aus Bytefolge (zufällig) eine Realzahl bilden
Oft bekommt man doch zufällige Gleitkommazahlen im Bereich [0,1) (siehe
![]() Da könntest du einfach die Mantisse/fraction zufällig wählen und den Exponenten (konstant) so setzen, das dieser Wertebereich herauskommt. (z.B. Aufteilung Double siehe ![]() Was sollte man mit einer Zufallszahl aus dem ganzen Double-Wertebereich anfangen? |
AW: Aus Bytefolge (zufällig) eine Realzahl bilden
Für die Berechnung einer Range wie beim original Random brauche ich nur 0<x<1, das stimmt.
Für Rückgabe eines Double wären nur 16 Stellen (Summe Vorkomma/Nachkomma), nicht der ganze Wertebereich interessant. Man spart sich halt weitere Berechnungen, wenn man Zufallszahlen im Double-Wertebereich braucht (z.B. Datum etc.). Aber wie gesagt, 0<x<1 reicht mir auch. Also nur die unteren 52 Bit (Mantisse) zufällig setzen und den Exponenten gezielt? Das klingt interessant, werde mich gleich mal mit dem genauen Aufbau auseinandersetzen. €: Hier die aktuelle Unit (noch mit der alten Methode für Double... experimentiere noch) Es ist nur die AnsiVersion importiert, da aber beide StringParameter nicht gebraucht werde, ist das erst mal unwichtig.
Delphi-Quellcode:
unit CryptRandom;
interface uses Windows; type HCryptProv = DWord; PCryptProv = ^HCryptProv; TCryptRandom = class private FProvider : HCryptProv; function GetContext: Boolean; procedure ReleaseContext; public constructor Create(); procedure GetBytes(Bytes : PByte; Size : Integer); function GetByte: Byte; function GetInteger: Integer; function GetCardinal: Cardinal; function GetInt64: Int64; function GetASCIIString(Size : Integer): AnsiString; function GetDouble: Double; function Random : Double; overload; function Random(Range : Integer): Integer; overload; end; { BOOL WINAPI CryptAcquireContext( __out HCRYPTPROV *phProv, __in LPCTSTR pszContainer, __in LPCTSTR pszProvider, __in DWORD dwProvType, __in DWORD dwFlags ); } function CryptAcquireContext(phProv : PProvider; pszContainer :PAnsiChar; pszProvider : PAnsiChar; dwProvType : DWord; dwFlags : DWord) : BOOL; stdcall; { BOOL WINAPI CryptReleaseContext( __in HCRYPTPROV hProv, __in DWORD dwFlags ); } function CryptReleaseContext(hProv : HCryptProv; dwFlags : DWord) :BOOL;stdcall; { BOOL WINAPI CryptGenRandom( __in HCRYPTPROV hProv, __in DWORD dwLen, __inout BYTE *pbBuffer ); } function CryptGenRandom(hProv : HCryptProv; dwLen : DWord; pbBuffer : PByte) : BOOL; stdcall; const CRYPT_VERIFYCONTEXT = $F0000000; // using ephemeral keys [default] CRYPT_NEWKEYSET = $8; // Creates a new key container "pszContainer" CRYPT_DELETEKEYSET = $10; // Delete the key container "pszContainer" PROV_RSA_FULL = 1; { PROV_RSA_AES PROV_RSA_SIG PROV_RSA_SCHANNEL PROV_DSS PROV_DSS_DH PROV_DH_SCHANNEL PROV_FORTEZZA PROV_MS_EXCHANGE PROV_SSL } AdvApiDll = 'AdvAPI32.DLL'; implementation function CryptAcquireContext; external AdvApiDll Name 'CryptAcquireContextA'; function CryptReleaseContext; external AdvApiDll Name 'CryptReleaseContext'; function CryptGenRandom; external AdvApiDll Name 'CryptGenRandom'; { TCryptRandom } constructor TCryptRandom.Create(); begin FProvider := 0; end; function TCryptRandom.GetContext: Boolean; begin CryptAcquireContext(@FProvider, NIL, NIL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); Result := GetLastError = 0; end; procedure TCryptRandom.ReleaseContext; begin if FProvider <> 0 then begin CryptReleaseContext(FProvider, 0); FProvider := 0; end; end; procedure TCryptRandom.GetBytes(Bytes : PByte; Size : Integer); begin if GetContext then begin try CryptGenRandom(FProvider, size, Bytes); finally ReleaseContext; end; end; end; function TCryptRandom.GetByte: Byte; begin GetBytes(@Result, SizeOf(Result)); end; function TCryptRandom.GetInteger: Integer; begin GetBytes(@Result, SizeOf(Result)); end; function TCryptRandom.GetCardinal: Cardinal; begin GetBytes(@Result, SizeOf(Result)); end; function TCryptRandom.GetInt64: Int64; begin GetBytes(@Result, SizeOf(Result)); end; function TCryptRandom.GetASCIIString(Size : Integer): AnsiString; var i : Integer; b : Byte; begin SetLength(Result, Size); GetBytes(@Result[1], Size); for i := 1 to Size do begin b := Ord(Result[i]); Result[i] := Chr((b div 4) + 33); end; end; function TCryptRandom.GetDouble: Double; var c : Currency; begin GetBytes(@c, SizeOf(c)); Result := c; end; function TCryptRandom.Random : Double; begin // 0 <= Result < 1, max 18 decimal digits Result := Frac(Abs(GetInt64 / 1000000000000000000)); end; function TCryptRandom.Random(Range : Integer): Integer; begin Result := Trunc(Random * Range); end; end. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:12 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz