2010-09-11 25 views
8

Tôi đang cố gắng tạo ra các phao ngẫu nhiên bằng cách sử dụng không có gì nhưng byte tôi nhận được từ/dev/urandom. Hiện tại, ý tưởng tốt nhất của tôi là để có được độ chính xác nền tảng như sau:Làm thế nào để thực hiện một hàm float ngẫu nhiên để nó không bị mất entropy? (PHP)

$maximumPrecision = strlen('' . 1/3) - 2; 

và sau đó xây dựng một chuỗi 0-9 trong vòng lặp số lần $ maximumPrecision cho chúng tôi biết. Đối với examle, nếu độ chính xác là 12, tôi sẽ tạo ra 12 số ngẫu nhiên và nối chúng. Tôi nghĩ đó là một ý tưởng tồi tệ.

Cập nhật: Điều này có hợp lý không?

$bytes =getRandomBytes(7); // Just a function that returns random bytes. 
$bytes[6] = $bytes[6] & chr(15); // Get rid off the other half 
$bytes .= chr(0); // Add a null byte 

$parts = unpack('V2', $bytes); 

$number = $parts[1] + pow(2.0, 32) * $parts[2]; 
$number /= pow(2.0, 52); 
+0

Số phải nằm trong khoảng từ 0 đến 1? – kennytm

+0

Có, một phao giữa 0 và 1 với độ chính xác của nền tảng. – Tower

+2

http://www.php.net/manual/de/function.rand.php#75794 – halfdan

Trả lời

4

Loại phao PHP thường được triển khai dưới dạng IEEE double. Định dạng này có độ chính xác 52 bit của mantissa, do đó về nguyên tắc, nó có thể tạo ra 2 khác nhau thống nhất số trong [0, 1).

Vì vậy, bạn có thể trích xuất 52 bit từ/dev/urandom, diễn giải dưới dạng số nguyên và chia cho 2 . For example:

// assume we have 52 bits of data, little endian. 
$bytes = "\x12\x34\x56\x78\x9a\xbc\x0d\x00"; 
//        ^ ^^ 12 bits of padding. 

$parts = unpack('V2', $bytes); 

$theNumber = $parts[1] + pow(2.0, 32) * $parts[2]; // <-- this is precise. 
$theNumber /= pow(2.0, 52);       // <-- this is precise. 
+1

Đúng. Mặc dù PHP theo C89, mà không yêu cầu một đôi IEEE cho kiểu 'double', nó sẽ khó để tìm một nền tảng mà nó không phải là trường hợp (mặc dù chúng có thể không hỗ trợ đầy đủ tiêu chuẩn). – Artefacto

+0

Thú vị, 'giải nghĩa như một số nguyên' thực sự có ý nghĩa gì trong trường hợp này? Tôi lấy 7 byte (56 bit) và ... – Tower

+0

@rFactor: Xem cập nhật. Bạn cũng có thể sử dụng "bit-shift" để xây dựng biểu diễn đôi thô và 'giải nén', nhưng đó là một mớ hỗn độn trong PHP. – kennytm

1

Vấn đề ở đây là rằng IEEE double precision number được xác định theo số mũ của cơ sở 2:

n = 2^exponent * 1.mantissa 

Vì bạn muốn có một số mũ -1, và không có số nguyên n2^n = 0.1 , nó trở nên phức tạp.

Điều này tạo số từ 1 đến 2. Bạn có thể trừ 1, nhưng bạn sẽ mất một lượng nhỏ entropy nếu bạn làm điều đó (câu trả lời của KennyTM mang lại một số trong phạm vi đó, và sử dụng tất cả entropy - câu trả lời này cố gắng tạo đại diện trực tiếp):

$source = fopen("/dev/urandom", "rb"); 

//build big-endian double 
//take only 32 bits because of 32-bit platforms 
$byte_batch_1 = fread($source, 4); //32-bit 
$byte_batch_2 = fread($source, 4); //32-bit, we only need 20 

$offset = (1 << 10) -1; 

$first_word = unpack("N", $byte_batch_2); 
$first_word = reset($first_word); 
$first_word &= 0xFFFFF; //leave only 20 lower bits 
$first_word |= $offset << 20; 

$str = pack("N", $first_word) . $byte_batch_1; 

//convert to little endian if necessary 
if (pack('s', 1) == "\x01\x00") { //little-endian 
    $str = strrev($str); 
} 

$float = unpack("d", $str); 
$float = reset($float); 
Các vấn đề liên quan