C中随机数的完全变化

时间:2013-04-24 03:01:09

标签: 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

我如何实现这一目标?我在软件编码方面没有太多经验

3 个答案:

答案 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 = 00x7ff]

的情况
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),无论您是否可以安全可靠地检测它们。

但这看起来像是一个单独的问题,可能已经被问过并回答了。