Полная вариация случайных чисел в C

Я пытаюсь создать 64-битные случайные числа, используя следующий код. Я хочу, чтобы числа в двоичном формате, но проблема в том, что я не могу изменить все биты. Я хочу, чтобы цифры менялись как можно больше

void PrintDoubleAsCBytes(double d, FILE* f) { f = fopen("tb.txt","a"); unsigned char a[sizeof(d)]; unsigned i; memcpy(a, &d, sizeof(d)); for (i = 0; i < sizeof(a); i++){ fprintf(f, "%0*X", (CHAR_BIT + 3) / 4, a[sizeof(d)-1-i]); } fprintf(f,"\n"); fclose(f); /*done!*/ } int main (int argc, char *argv) { int limit = 100 ; double a, b; double result; int i ; printf("limit = %d", limit ); for (i= 0 ; i< limit;i++) { a= rand(); b= rand(); result = a * b; printf ("A= %f B = %f\n",a,b); printf ("result= %f\n",result); PrintDoubleAsCBytes(a, stdout); puts(""); PrintDoubleAsCBytes(b, stdout); puts(""); PrintDoubleAsCBytes(result, stdout); puts(""); } } 

ВЫХОДНОЙ ФАЙЛ

 41DAE2D159C00000 //Last bits remain zero, I want them to change as well as in case of the result 41C93D91E3000000 43B534EE7FAEB1C3 41D90F261A400000 41D98CD21CC00000 43C4021C95228080 41DD2C3714400000 41B9495CFF000000 43A70D6CAD0EE321 

Как я могу это сделать? У меня нет большого опыта в программном кодировании

В Java это очень просто:

 Random rng = new Random(); //do this only once long randLong = rng.NextLong(); double randDoubleFromBits = Double.longBitsToDouble(randLong); 

В CI знаю только способ взломать это 🙂


Поскольку RAND_MAX может быть как минимум 2 ^ 15-1, но определена реализация, возможно, вы можете получить 64 случайных бита из rand() , выполнив маски и бит-сдвиги:

 //seed program once at the start srand(time(NULL)); uint64_t a = rand()&0x7FFF; uint64_t b = rand()&0x7FFF; uint64_t c = rand()&0x7FFF; uint64_t d = rand()&0x7FFF; uint64_t e = rand()&0x7FFF; uint64_t random = (a<<60)+(b<<45)+(c<<30)+(d<<15)+e; 

Затем введите его в объединение и используйте другой член объединения, чтобы интерпретировать его биты как double . Что-то вроде

 union { double d; long l; } doubleOrLong; doubleOrLong.l = random; double randomDouble = doubleOrLong.d; 

(Я не тестировал этот код)


EDIT: Объяснение того, как оно должно работать

Во-первых, srand(time(NULL)); семена rand с текущей меткой времени. Поэтому вам нужно только сделать это один раз с самого начала, и если вы хотите воспроизвести более раннюю серию RNG, вы можете повторно использовать это семя, если хотите.

rand() возвращает случайное, несмещенное целое число от 0 до RAND_MAX включительно. RAND_MAX должен быть не менее 2 ^ 15-1, что составляет 0x7FFF. Чтобы написать программу таким образом, что не имеет значения, что такое RAND_MAX (например, это может быть 2 ^ 16-1, 2 ^ 31-1, 2 ^ 32-1 ...), мы маскируем все, кроме дна 15 бит - 0x7FFF равно 0111 1111 1111 1111 в двоичном или нижнем 15 бит.

Теперь нам нужно собрать все 15 случайных бит в 64 бит. Оператор битдвига, << , сдвигает левый операнд (правый операнд) влево. Таким образом, окончательный uint64_t, который мы называем случайным, имеет случайные биты, полученные из других переменных:

aaaa bbbb bbbb bbbb bbbc cccc cccc cccc ccdd dddd dddd dddd deee eeee eeee eeee

Но это все еще рассматривается как uint64_t, а не как двойной. Это неопределенное поведение для этого, поэтому вы должны убедиться, что он работает так, как вы ожидаете от своего компилятора, но если вы поместите этот uint64_t в объединение, а затем прочитаете другой двойной член союза, то вы (надеюсь)! интерпретировать те же самые биты, что и двойной, состоящий из случайных бит.

В зависимости от вашей платформы, но при условии, что IEEE 754, например, Wikipedia , почему бы не обрабатывать внутренний двойной формат?

(Ошибки запрета), это генерирует случайные, но действительные удваивает. [Не совсем охватили все базы здесь, например, случай, когда exp = 0 или 0x7ff ]

 double randomDouble() { uint64_t buf = 0ull; // sign bit bool odd = rand()%2 > 0; if (odd) buf = 1ull<<63; // exponent int exponentLength = 11; int exponentMask = (1 << exponentLength) - 1; int exponentLocation = 63 - exponentLength; uint64_t exponent = rand()&exponentMask; buf += exponent << exponentLocation; // fraction int fractionLength = exponentLocation; int fractionMask = (1 << exponentLocation) - 1; // Courtesy of Patashu uint64_t a = rand()&0x7FFF; uint64_t b = rand()&0x7FFF; uint64_t c = rand()&0x7FFF; uint64_t d = rand()&0x7FFF; uint64_t fraction = (a<<45)+(b<<30)+(c<<15)+d; fraction = fraction& fractionMask; buf += fraction; double* res = reinterpret_cast(&buf); return *res; } 

Использование может использовать следующее:

 void GenerateRandomDouble(double* d) { unsigned char* p = (unsigned char*)d; unsigned i; for (i = 0; i < sizeof(d); i++) p[i] = rand(); } 

Проблема с этим методом заключается в том, что ваша программа на C может не использовать некоторые из значений, возвращаемых этой функцией, потому что они являются недопустимыми или специальными значениями с плавающей запятой.

Но если вы тестируете свое оборудование, вы можете генерировать случайные байты и напрямую загружать их в указанное оборудование без предварительного преобразования их в double .

Единственное место, где вам нужно рассматривать эти случайные байты как double является точкой проверки результатов, возвращаемых аппаратным обеспечением.

В этот момент вам нужно посмотреть байты и посмотреть, представляют ли они действительное значение. Если это так, вы можете memcpy() использовать байты в double и использовать его.

Следующая проблема, с которой приходится иметь дело, это переполнение / недополнение и исключения из-за того, что вам нужно делать с этими случайными doubles (сложение, умножение и т. Д.). Вам нужно выяснить, как справиться с ними на вашей платформе (компилятор + ЦП + ОС), независимо от того, можете ли вы безопасно и надежно их обнаружить.

Но это выглядит как отдельный вопрос, и его, вероятно, уже спросили и ответили.