How to implement a fast RNG? - delphi
I am trying to port an existing random generator based on 128 bit XorShift from C. But I have trouble with generating the seed which is just generating the same number again and again.
static uint64_t s[ 2 ];
static uint64_t __inline next(void) {
uint64_t s1 = s[ 0 ];
const uint64_t s0 = s[ 1 ];
s[ 0 ] = s0;
s1 ^= s1 << 23;
return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0;
}
uint64_t getusertime() {
struct rusage rusage;
getrusage( 0, &rusage );
return rusage.ru_utime.tv_sec * 1000000ULL + ( rusage.ru_utime.tv_usec / 1000 ) * 1000;
}
int main( int argc, char* argv[] ) {
const long long int n = strtoll( argv[1], NULL, 0 );
uint64_t t = 0;
for( int i = 0; i < 2; i++ ) s[ i ] = -1ULL / 3;
const int64_t start = getusertime();
for( long long int i = n; i-- != 0; ) t ^= next();
const int64_t elapsed = getusertime() - start;
const double secs = elapsed / 1E6;
printf( "%f s, %.02f queries/s, %.02f ns/query\n", secs, n / secs, 1E9 * secs / n );
if ( t == 0 ) putchar( 0 );
return 0;
}
program Project1;
var
S: Array [0..1] of UInt64;
function XorShift128: UInt64;
var
s0, s1: UInt64;
begin
s1 := s[0];
s0 := s[1];
s[0] := s0;
s1 := s1 xor (s1 shl 23);
s[1] := (s1 xor s0 xor (s1 shr 17) xor (s0 shr 26));
Result := s[1] + s0;
end;
procedure GenerateSeed;
var
I: Integer;
begin
for I := 0 to High(S) do
S[I] := MaxLongInt div 3;
end;
var
I: UInt64;
begin
GenerateSeed;
I := XorShift128;
end.
The reason you get the same value every time you run the program in the question is that you use the same seed every time. If I am understanding your comments correctly. The other difference between the C and the Pascal is the seed – see below.
However, your code is fine and is an accurate translation of the C code. The output of this C program:
#include <stdio.h>
#include <stdint.h>
static uint64_t s[ 2 ];
static uint64_t __inline next(void) {
uint64_t s1 = s[ 0 ];
const uint64_t s0 = s[ 1 ];
s[ 0 ] = s0;
s1 ^= s1 << 23;
return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0;
}
int main(void)
{
s[ 0 ] = s[ 1 ] = 715827882; // the value of MaxLongInt div 3
printf("%llu\n", next());
printf("%llu\n", next());
printf("%llu\n", next());
return 0;
}
is
6004846026386057
6004846115863870
12676181551404632061
The output of this Delphi program:
program Project1;
{$APPTYPE CONSOLE}
var
S: Array [0..1] of UInt64;
function XorShift128: UInt64;
var
s0, s1: UInt64;
begin
s1 := s[0];
s0 := s[1];
s[0] := s0;
s1 := s1 xor (s1 shl 23);
s[1] := (s1 xor s0 xor (s1 shr 17) xor (s0 shr 26));
Result := s[1] + s0;
end;
procedure GenerateSeed;
var
I: Integer;
begin
for I := 0 to High(S) do
S[I] := MaxLongInt div 3;
end;
begin
GenerateSeed;
Writeln(XorShift128);
Writeln(XorShift128);
Writeln(XorShift128);
end.
is
6004846026386057
6004846115863870
12676181551404632061
I note that the C code in the question uses a different seed from your translation. It seeds the state with -1ULL / 3 and that leads to this output:
46820872945684
46912499612351
13066320939010318272
To match that in the Delphi code you would use high(UInt64) div 3. Do that and you get the output above.
An important note here is that your Delphi code only supplies 64 bits of seed, but your C code supplies 128. I expect that you should supply 128 bits of seed.
Related
Is Delphi's Skewness correct
In Delphi one can calculate Skewness using System.Math's function MomentSkewKurtosis(). var m1, m2, m3, m4, skew, k: Extended; System.Math.MomentSkewKurtosis([1.1, 3.345, 12.234, 11.945, 14.235, 16.876, 20.213, 11.001, 7.098, 21.234], m1, m2, m3, m4, skew, k); This will prints skew equal to -0.200371489809269. Minitab prints the value -0.24 SigmaXL prints the value -0.23611 The reason is that Delphi does not not perform adjustment. Here is my implementation which calculates adjustment: function Skewness(const X: array of Extended; const Adjusted: Boolean): Extended; begin var AMean := Mean(X); var xi_minus_mean_power_3 := 0.0; var xi_minus_mean_power_2 := 0.0; for var i := Low(X) to High(X) do begin xi_minus_mean_power_3 := xi_minus_mean_power_3 + IntPower((X[i] - AMean), 3); xi_minus_mean_power_2 := xi_minus_mean_power_2 + IntPower((X[i] - AMean), 2); end; // URL : https://www.gnu.org/software/octave/doc/v4.0.1/Descriptive-Statistics.html { mean ((x - mean (x)).^3) skewness (X) = ------------------------. std (x).^3 } var N := Length(X); Result := xi_minus_mean_power_3 / N / IntPower(Sqrt(1 / N * xi_minus_mean_power_2), 3); // URL : https://www.gnu.org/software/octave/doc/v4.0.1/Descriptive-Statistics.html { sqrt (N*(N-1)) mean ((x - mean (x)).^3) skewness (X, 0) = -------------- * ------------------------. (N - 2) std (x).^3 } if Adjusted then Result := Result * Sqrt(N * Pred(N)) / (N - 2); end; The helper routine IntPower is as follows: function IntPower(const X: Extended; const N: Integer): Extended; /// <remarks> /// Calculate any float to non-negative integer power. Developed by Rory Daulton and used with permission. Last modified December 1998. /// </remarks> function IntPow(const Base: Extended; const Exponent: LongWord): Extended; { Heart of Rory Daulton's IntPower: assumes valid parameters & non-negative exponent } {$IFDEF WIN32} asm fld1 // Result := 1 cmp eax, 0 // eax := Exponent jz ##3 fld Base jmp ##2 ##1: fmul ST, ST // X := Base * Base ##2: shr eax,1 jnc ##1 fmul ST(1),ST // Result := Result * X jnz ##1 fstp st // pop X from FPU stack ##3: fwait end; {$ENDIF} {$IFDEF WIN64} begin Result := Power(Base, Exponent); end; {$ENDIF} begin if N = 0 then Result := 1 else if (X = 0) then begin if N < 0 then raise EMathError.Create('Zero cannot be raised to a negative power.') else Result := 0 end else if (X = 1) then Result := 1 else if X = -1 then begin if Odd (N) then Result := -1 else Result := 1 end else if N > 0 then Result := IntPow (X, N) else begin var P: LongWord; if N <> Low (LongInt) then P := Abs(N) else P := LongWord(High(LongInt)) + 1; try Result := IntPow(X, P); except on EMathError do begin Result := IntPow(1 / X, P); // try again with another method, perhaps less precise Exit; end; end; Result := 1 / Result; end; end; With that function the adjusted skewness becomes the accurate -0.237611357234441 matching Matlab and Minitab. The only explanation I found is: https://octave.org/doc/v4.0.1/Descriptive-Statistics.html "The adjusted skewness coefficient is obtained by replacing the sample second and third central moments by their bias-corrected versions." Same goes with Kurtosis: function Kurtosis(const X: array of Extended; const Adjusted: Boolean): Extended; begin var AMean := Mean(X); var xi_minus_mean_power_4 := 0.0; var xi_minus_mean_power_2 := 0.0; for var i := Low(X) to High(X) do begin xi_minus_mean_power_4 := xi_minus_mean_power_4 + IntPower((X[i] - AMean), 4); xi_minus_mean_power_2 := xi_minus_mean_power_2 + IntPower((X[i] - AMean), 2); end; { mean ((x - mean (x)).^4) k1 = ------------------------ std (x).^4 } var N := Length(X); Result := xi_minus_mean_power_4 / N / IntPower(1 / N * xi_minus_mean_power_2, 2); { N - 1 k0 = 3 + -------------- * ((N + 1) * k1 - 3 * (N - 1)) (N - 2)(N - 3) } if Adjusted then // Mathlab, Minitab and SigmaXL do not add 3 (which is the kurtosis for normal distribution Result := {3 + }(N - 1) / ((N - 2) * (N - 3)) * ((N + 1) * Result - 3 * (N - 1)); end; What is the reason for such adjustments and why Delphi decided not to implement it?
Code for Chi-square distribution function in Delphi
I have been looking for usable and full code for chi-square distribution in Delphi. There are some codes via net, but usually they don't work or have missing parts, do not compile etc.. There are also some libraries, but I'm interested about some code that I just can simply implement. I've found something almost working. Some german parts have been fixed, it compiles and it gives p-values for most of the data: function LnGamma (x : Real) : Real; const a0 = 0.083333333096; a1 = -0.002777655457; a2 = 0.000777830670; c = 0.918938533205; var r : Real; begin r := (a0 + (a1 + a2 / sqr(x)) / sqr(x)) / x; LnGamma := (x - 0.5) * ln(x) - x + c + r; end; function LnFak (x : Real) : Real; var z : Real; begin z := x+1; LnFak := LnGamma(z); end; function Reihe (chi : Real; f : Real) : Real; const MaxError = 0.0001; var Bruch, Summe, Summand : Real; k, i : longint; begin Summe := 1; k := 1; repeat Bruch := 1; for i := 1 to k do Bruch := Bruch * (f + 2 * i); Summand := power(chi, 2 * k) / Bruch; Summe := Summe + Summand; k := succ(k); until (Summand < MaxError); Reihe := Summe; end; function IntegralChi (chisqr : Real; f : longint) : Real; var s : Real; begin S := power((0.5 * chisqr), f/2) * Reihe(sqrt(chisqr), f) * exp((-chisqr/2) - LnGamma((f + 2) / 2)); IntegralChi := 1 - s; end; It works quite good for relatively big results. For example: For Chi = 1.142132 and df = 1 I'm getting p about 0.285202, which is perfect. Same as SPSS result or other programs. But for example Chi = 138.609137 and df = 4 I should recieive something about 0.000000, but I'm getting floating point overflow error in Reiche function. Summe and Summand are very big then. I admit that understanding distribution function is not my strong point, so maybe someone will tell me what I did wrong? Thank you very much for the information
You should debug your program and find that there is an overflow in your loop for k=149. For k=148 the value of Bruch is 3.3976725289e+304. The next computation of Bruch overflows. A fix is to code for i := 1 to k do Bruch := Bruch / (f + 2 * i); Summand := power(chi, 2 * k) * Bruch; With this change you get the value IntegralChi(138.609137,4) = 1.76835197E-7 after 156th iteration. Note that your computation (even for this simple algorithm) is sub-optimal because you compute the Bruch value over and over again. Just update it once per loop: function Reihe (chi : Real; f : Real) : Real; const MaxError = 0.0001; var Bruch, Summe, Summand : Real; k : longint; begin Summe := 1; k := 1; Bruch := 1; repeat Bruch := Bruch / (f + 2 * k); Summand := power(chi, 2 * k) * Bruch; Summe := Summe + Summand; k := succ(k); until (Summand < MaxError); Reihe := Summe; end; Similar consideration should be applied to compute power(chi, 2*k) and then combine this with the improved evaluation of Bruch. Edit: As a response to your comment, here the improved version based on the property of the power function, that is power(chi, 2*(k+1)) = power(chi, 2*k)*sqr(chi) function Reihe (chi : Real; f : Real) : Real; const MaxError = 0.0001; var chi2, Summe, Summand : Real; k : longint; begin Summe := 1; k := 1; Summand := 1; chi2 := sqr(chi); repeat Summand := Summand * chi2 / (f + 2 * k); Summe := Summe + Summand; k := succ(k); until (Summand < MaxError); Reihe := Summe; end;
How to pass an integer constant as generic parameter
I need to pass an integer constant as a generic parameter. For example, here is a code for working with a non-standard floating-point numbers (float16) (the code is taken from the githab and slightly modified): const ES = 5; // exponent size (bits) MS = 10; // mantissa size (bits) // plus sign (1 bit) ESMS = ES + MS; function FloatXXToSingle(const _in: cardinal): Single; var t1, t2, t3: cardinal; begin t1 := _in and (Cardinal(1 shl ESMS) - 1); // Non-sign bits t2 := _in and (1 shl ESMS); // Sign bit t3 := _in and ((1 shl ES - 1) shl MS); // Exponent t1 := t1 shl (23 - MS); // Align mantissa on MSB t2 := t2 shl (31 - ESMS); // Shift sign bit into position t1 := t1 + cardinal((127-(1 shl (ES-1)-1)) shl 23); // Adjust bias if t3 = 0 then // Denormals-as-zero t1 := 0; t1 := t1 or t2; // Re-insert sign bit pCardinal(#Result)^ := t1; end; I want to create a generic on its basis, so that I can set the number of bits for the exponent and mantissa myself. Now I'm doing something like this: type TFloat<TES, TMS> = class const ES = sizeof(TES); const MS = sizeof(TMS); const ESMS = ES + MS; class function ToSingle(const _in: cardinal): Single; end; class function TFloat<TES, TMS>.ToSingle(const _in: cardinal): Single; var t1, t2, t3: cardinal; begin t1 := _in and (Cardinal(1 shl ESMS) - 1); // Non-sign bits t2 := _in and (1 shl ESMS); // Sign bit t3 := _in and ((1 shl ES - 1) shl MS); // Exponent t1 := t1 shl (23 - MS); // Align mantissa on MSB t2 := t2 shl (31 - ESMS); // Shift sign bit into position t1 := t1 + cardinal((127-(1 shl (ES-1)-1)) shl 23); // Adjust bias if t3 = 0 then // Denormals-as-zero t1 := 0; t1 := t1 or t2; // Re-insert sign bit pCardinal(#Result)^ := t1; end; But to use this, I have to write something like this: type TMyFloat = TFloat<packed record a:Integer; b:Byte; end, packed record a: Extended; end>; begin writeln(TMyFloat.ToSingle($1234)); end. But this method is not at all elegant. Perhaps there is a way to directly pass two numbers to the generic: the size of the exponent and the mantissa?
Delphi - Lenze Standard addressing - Code number conversion
From Lenze manual Code number (C1, C2) Standard addressing The meaning of the code numbers and the assigned parameters can be obtained from the code table (see chapter 8.2). When transmitting data, the code number are coded as follows: The following calculation determines the two ASCII digits from the code number (value range: 0..6229) (value range: 48dec 127dec): C1 = INTEGER((REMAINDER(code number/790))/10) + 48dec C2 = REMAINDER(REMAINDER(code number/790)/10) + INTEGER(code number/790) x 10 + 48dec Procedure for calculating C1 and C2 from codenumber. procedure pCodeNumberToC1C2(CodeNumber: Word; var C1, C2: Byte); begin C1 := Byte((CodeNumber mod 790) div 10) + 48; C2 := ((CodeNumber mod 790) mod 10) + 48 + 10 * Byte(CodeNumber div 790); end; But, how to calculate it the other way without the aweful: function fC1C2ToCodeNumber(iC1, iC2: Byte): Word; var C1, C2: Byte; i: Integer; Begin Result := 0; For i := 0 to 6229 Do Begin pCodeNumberToC1C2(i, C1, C2); if (C1 = iC1) and (C2 = iC2) Then Result := i; End; Result := cn; End;
Let's N = p * 790 + q then c1 = 48 + q div 10 c2 = 48 + q mod 10 + 10 * p so p = (c2-48) div 10 q = (c2-48) mod 10 + (c1-48) * 10 test: var c1, c2: Byte; n, p, q, t: Word; begin for t := 0 to 6229 do begin n := t; pCodeNumberToC1C2(n, c1, c2); p := (c2-48) div 10; q := (c2-48) mod 10 + (c1-48) * 10; n := 790*p+q; if n <> t then Memo1.Lines.Add('Failed at ' + IntToStr(t)) end; Final: function C1C2ToCodeNumber(C1, C2: Byte): Word; begin Result := ((C2 - 48) div 10) * 790 + ((C2 - 48) mod 10 + (C1 - 48) * 10); end;
As an alternative to arithmetic you could consider a lookup table. At the cost of memory, this gives you better performance. The code looks like this: const CodeNumberTable: array [48..126, 48..127] of Word = ( .... code removed because of Sack Overflow post size limitation ); const MinC1 = low(CodeNumberTable); MinC2 = high(CodeNumberTable); MaxC1 = low(CodeNumberTable[MinC1]); MaxC2 = high(CodeNumberTable[MinC1]); type EInvalidParameters = class(Exception); function fC1C2ToCodeNumber(iC1, iC2: Byte): Word; begin if not InRange(iC1, MinC1, MaxC1) then raise EInvalidParameters.CreateFmt( 'iC1 (%d) must be in the range %d to %d', [iC1, MinC1, MaxC1] ); if not InRange(iC2, MinC2, MaxC2) then raise EInvalidParameters.CreateFmt( 'iC2 (%d) must be in the range %d to %d', [iC2, MinC2, MaxC2] ); Result := CodeNumberTable[iC1, iC2]; if Result=high(Word) then raise EInvalidParameters.CreateFmt( 'CodeNumber not defined for iC1=%d, ic2=%d', [iC1, iC2] ); end; I can supply the table via paste bin if you are interested.
CRC-CCITT (0xFFFF) function?
Can someone help me with Delphi implementation of CRC-CCITT (0xFFFF)? Already get the Java version, but confusing on how to port it to Delphi public static int CRC16CCITT(byte[] bytes) { int crc = 0xFFFF; // initial value int polynomial = 0x1021; // 0001 0000 0010 0001 (0, 5, 12) for (byte b : bytes) { for (int i = 0; i < 8; i++) { boolean bit = ((b >> (7-i) & 1) == 1); boolean c15 = ((crc >> 15 & 1) == 1); crc <<= 1; if (c15 ^ bit) crc ^= polynomial; } } crc &= 0xffff; //System.out.println("CRC16-CCITT = " + Integer.toHexString(crc)); return crc; } and for PHP implementation <?php function crc16($data) { $crc = 0xFFFF; for ($i = 0; $i < strlen($data); $i++) { $x = (($crc >> 8) ^ ord($data[$i])) & 0xFF; $x ^= $x >> 4; $crc = (($crc << 8) ^ ($x << 12) ^ ($x << 5) ^ $x) & 0xFFFF; } return $crc; }
0xFFFF translates to $FFFF & translates to and ^ translates to xor << translates to shl >> translates to shr x ^= y translates to x := x xor y, similar for &=, <<=, etc. These operators generally have higher precedence in Delphi so they usually need to have their arguments parenthesized. I'm quite sure that there are plenty of other implementations of CRC16 etc. for Delphi, see e.g. Improve speed on Crc16 calculation
function CRC16CCITT(bytes: TBytes): Word; const polynomial = $1021; // 0001 0000 0010 0001 (0, 5, 12) var crc: Word; I, J: Integer; b: Byte; bit, c15: Boolean; begin crc := $FFFF; // initial value for I := 0 to High(bytes) do begin b := bytes[I]; for J := 0 to 7 do begin bit := (((b shr (7-J)) and 1) = 1); c15 := (((crc shr 15) and 1) = 1); crc := crc shl 1; if ((c15 xor bit) <> 0) then crc := crc xor polynomial; end; end; Result := crc and $ffff; end;
You can find one in Delphi Encryption Compendium (DEC) component. 5 Checksums (CRC32, CRC16-CCITT, CRC16-Standard ...) http://blog.digivendo.com/2008/11/delphi-encryption-compendium-dec-52-for-d2009-released/
i found some code that works: function crc16(Buffer:String;Polynom,Initial:Cardinal):Cardinal; var i,j: Integer; begin Result:=Initial; for i:=1 to Length(Buffer) do begin Result:=Result xor (ord(buffer[i]) shl 8); for j:=0 to 7 do begin if (Result and $8000)<>0 then Result:=(Result shl 1) xor Polynom else Result:=Result shl 1; end; end; Result:=Result and $ffff; end; source : http://www.miscel.dk/MiscEl/CRCcalculations.html
unit CRC16CCITT; interface function ComputeCRC16CCITT(crc: word; const data: PByte; len:integer) : word; implementation const crc16_table: array [0..$FF] of word = (0,4489,8978,12955,17956,22445,25910,29887,35912,40385,44890,48851,51820,56293,59774, 63735,4225,264,13203,8730,22181,18220,30135,25662,40137,36160,49115,44626,56045,52068,63999, 59510,8450,12427,528,5017,26406,30383,17460,21949,44362,48323,36440,40913,60270,64231,51324, 55797,12675,8202,4753,792,30631,26158,21685,17724,48587,44098,40665,36688,64495,60006,55549, 51572,16900,21389,24854,28831,1056,5545,10034,14011,52812,57285,60766,64727,34920,39393,43898, 47859,21125,17164,29079,24606,5281,1320,14259,9786,57037,53060,64991,60502,39145,35168,48123, 43634,25350,29327,16404,20893,9506,13483,1584,6073,61262,65223,52316,56789,43370,47331,35448, 39921,29575,25102,20629,16668,13731,9258,5809,1848,65487,60998,56541,52564,47595,43106,39673, 35696,33800,38273,42778,46739,49708,54181,57662,61623,2112,6601,11090,15067,20068,24557,28022, 31999,38025,34048,47003,42514,53933,49956,61887,57398,6337,2376,15315,10842,24293,20332,32247, 27774,42250,46211,34328,38801,58158,62119,49212,53685,10562,14539,2640,7129,28518,32495,19572, 24061,46475,41986,38553,34576,62383,57894,53437,49460,14787,10314,6865,2904,32743,28270,23797, 19836,50700,55173,58654,62615,32808,37281,41786,45747,19012,23501,26966,30943,3168,7657,12146, 16123,54925,50948,62879,58390,37033,33056,46011,41522,23237,19276,31191,26718,7393,3432,16371, 11898,59150,63111,50204,54677,41258,45219,33336,37809,27462,31439,18516,23005,11618,15595,3696, 8185,63375,58886,54429,50452,45483,40994,37561,33584,31687,27214,22741,18780,15843,11370,7921, 3960); function ComputeCRC16CCITT(crc: word; const data: PByte; len:integer) : word; var i : integer; begin for i := 0 to len-1 do crc := (crc shr 8) xor crc16_table[(crc xor data[i]) and $ff]; result := crc; end; end.