我正在尝试使用以下代码生成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
我如何实现这一目标?我在软件编码方面没有太多经验
答案 0 :(得分:3)
在Java中很容易:
Random rng = new Random(); //do this only once
long randLong = rng.NextLong();
double randDoubleFromBits = Double.longBitsToDouble(randLong);
在C中我只知道一种黑客的方法:)
由于RAND_MAX可以低至2 ^ 15-1但是是实现定义的,你可以通过掩码和位移来获得rand()
中的64个随机位:
//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;
然后将它填入并集中并使用union的另一个成员将其位解释为double
。像
union
{
double d;
long l;
} doubleOrLong;
doubleOrLong.l = random;
double randomDouble = doubleOrLong.d;
(我还没有测试过这段代码)
编辑:解释它应该如何工作
首先,srand(time(NULL));
种子随当前时间戳而变化。因此,您只需要在开始时执行此操作一次,如果您想要重现早期的RNG系列,您可以根据需要重复使用该种子。
rand()
返回0到RAND_MAX(包括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位。 bitshift运算符<<
将左操作数(右操作数)位向左移位。因此,我们称为随机的最终uint64_t具有从其他变量派生的随机位,如下所示:
aaaa bbbb bbbb bbbb bbbc cccc cccc cccc ccdd dddd dddd dddd deee eeee eeee eeee
但是这仍然被视为uint64_t,而不是双倍。这样做是未定义的行为,所以你应该确保它在你选择的编译器上按预期的方式工作,但是如果你把这个uint64_t放在一个union中然后读取union的另一个double成员,那么你(希望!)将那些相同的位解释为由随机位组成的双精度。
答案 1 :(得分:1)
取决于您的平台,但假设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<double*>(&buf);
return *res;
}
答案 2 :(得分:0)
使用可以使用:
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
(加法,乘法等)所做的任何事情而导致的异常。您需要弄清楚如何在您的平台上处理它们(编译器+ CPU + OS),无论您是否可以安全可靠地检测它们。
但这看起来像是一个单独的问题,可能已经被问过并回答了。