Get a random double value (floating point) from a random byte array between 0 and 1 in C #?

Suppose I have an array of bytes that are really random (e.g., captured from an entropy source).

byte[] myTrulyRandomBytes = MyEntropyHardwareEngine.GetBytes(8);

Now I want to get a random floating point value with double precision, but between values ​​0 and positive 1 (as a function Random.NextDouble()).

Just passing an array of 8 random bytes to BitConverter.ToDouble()can give strange results, but most importantly, the results will almost never be less than 1.

I'm good at bit manipulation, but formatting floating point numbers has always been cryptic to me. I tried many combinations of bits to apply randomness and always found numbers that were either just over 1, always VERY close to 0 or very large.

Can someone explain which bits should be made random in doubleto make it random within a range of 0 and 1?

+4
source share
6 answers

It is easier than you think; its all about scaling (also true when moving from 0-1 range to another range).

Basically, if you know that you have 64 truly random bits (8 bytes), just do this:

double zeroToOneDouble = (double)(BitConverter.ToUInt64(bytes) / (decimal)ulong.MaxValue);

, "" . , , Mersenne Twister.

+5

, , , :

long asLong = BitConverter.ToInt64(myTrulyRandomBytes, 0);
double number = (double)(asLong & long.MaxValue) / long.MaxValue;

ulong double , , :

 vxorps      xmm0,xmm0,xmm0 
 vcvtsi2sd   xmm0,xmm0,rcx   ; interpret ulong as long and convert it to double
 test        rcx,rcx         ; add fixup if it was "negative"
 jge         000000000000001D 
 vaddsd      xmm0,xmm0,mmword ptr [00000060h] 
 vdivsd      xmm0,xmm0,mmword ptr [00000068h] 

:

 vxorps      xmm0,xmm0,xmm0 
 vcvtsi2sd   xmm0,xmm0,rcx 
 vdivsd      xmm0,xmm0,mmword ptr [00000060h] 

x64 JIT .NET 4, , ulong double.

, : 2 62 0.0 1.0 , , .

, , ulong 1,0 , . , 1.0, ( . , , ):

long asLong = BitConverter.ToInt64(myTrulyRandomBytes, 0);
double number = (double)(asLong & long.MaxValue) / ((double)long.MaxValue + 1);

( )

long asLong = BitConverter.ToInt64(myTrulyRandomBytes, 0);
double number = (double)(asLong & long.MaxValue) * 1.08420217248550443400745280086994171142578125E-19;

ulong, .


, double -bits, .

- /, ( ), , ( , 0, ).

, a double . 0 1, , 1 2, 1.

, , :

x &= (1L << 52) - 1;

( 1.0 - 2.0, 2)

x |= 0x3ff0000000000000;

1:

return BitConverter.Int64BitsToDouble(x) - 1;

. , , 52, 53. ( ).


, .

(u) (u) , , , . , ( ), () : ( )

float distribution

" " , . , , .

Top - , , , (u) . , (u) longs . , "" .

- , , ( 0,5 1,0) , , , " ". - , .

NextDouble System.Random int 0.0.. 1.0. , , , . int, int.MaxValue 1/(2 31 -1) ( , ), 33 , - .

int.MaxValue , - , NextDouble , , :

const double scale = 4.6566128752458E-10;
double prev = 0;
Dictionary<long, int> hist = new Dictionary<long, int>();
for (int i = 0; i < int.MaxValue; i++)
{
    long bits = BitConverter.DoubleToInt64Bits(i * scale - prev);
    if (!hist.ContainsKey(bits))
        hist[bits] = 1;
    else
        hist[bits]++;
    prev = i * scale;
    if ((i & 0xFFFFFF) == 0)
        Console.WriteLine("{0:0.00}%", 100.0 * i / int.MaxValue);
}
+5

, , :

ulong asLong = BitConverter.ToUInt64(myTrulyRandomBytes, 0);
double number = (double)asLong / ulong.MaxValue;

, , ulong, , 0 1.

+3

long 0 1, :

long longValue = BitConverter.ToInt64(myTrulyRandomBytes, 0);
longValue &= 0x3fefffffffffffff;

[0, 1).
. 0x3fefffffffffffff 1 1, 1.

, 1. :

longValue |= 0x03c00000000000000;

: dotnetfiddle.

+2

, , .

, Int64BitsToDouble , , NaN . , 0x7ff0000000000001, , NaN ( ).

, , , , , NaN, , , . (0, 1), , , .

To be safe, just use ToInt32 and use this int as a seed for Random. (To be safer, unscrew 0.) It will not be as fast as other circuits, but it will be much safer. A lot of research and effort has been made to make RNGs in good ways that are not immediately obvious.

+2
source

A simple code snippet for printing bits for you.

for (double i = 0; i < 1.0; i+=0.05)
{
    var doubleToInt64Bits = BitConverter.DoubleToInt64Bits(i);
    Console.WriteLine("{0}:\t{1}", i, Convert.ToString(doubleToInt64Bits, 2));
}

0.05:   11111110101001100110011001100110011001100110011001100110011010
0.1:    11111110111001100110011001100110011001100110011001100110011010
0.15:   11111111000011001100110011001100110011001100110011001100110100
0.2:    11111111001001100110011001100110011001100110011001100110011010
0.25:   11111111010000000000000000000000000000000000000000000000000000
0.3:    11111111010011001100110011001100110011001100110011001100110011
0.35:   11111111010110011001100110011001100110011001100110011001100110
0.4:    11111111011001100110011001100110011001100110011001100110011001
0.45:   11111111011100110011001100110011001100110011001100110011001100
0.5:    11111111011111111111111111111111111111111111111111111111111111
0.55:   11111111100001100110011001100110011001100110011001100110011001
0.6:    11111111100011001100110011001100110011001100110011001100110011
0.65:   11111111100100110011001100110011001100110011001100110011001101
0.7:    11111111100110011001100110011001100110011001100110011001100111
0.75:   11111111101000000000000000000000000000000000000000000000000001
0.8:    11111111101001100110011001100110011001100110011001100110011011
0.85:   11111111101011001100110011001100110011001100110011001100110101
0.9:    11111111101100110011001100110011001100110011001100110011001111
0.95:   11111111101110011001100110011001100110011001100110011001101001
+1
source

Source: https://habr.com/ru/post/1675383/


All Articles