3

我正在尝试使用以下代码生成 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

我该如何做到这一点?我在软件编码方面没有太多经验

4

3 回答 3

3

在 Java 中,这很容易:

Random rng = new Random(); //do this only once

long randLong = rng.NextLong();
double randDoubleFromBits = Double.longBitsToDouble(randLong);

在 CI 中只知道一种 hack 方法:)


由于 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;

然后将其填充到一个联合中,并使用联合的另一个成员将其位解释为double. 就像是

union
{
    double d;
    long l;
} doubleOrLong;

doubleOrLong.l = random;
double randomDouble = doubleOrLong.d;

(我没有测试过这段代码)


编辑:解释它应该如何工作

首先,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 位。移位运算符<<将左操作数(右操作数)位向左移动。因此,我们称为 random 的最终 uint64_t 具有从其他变量派生的随机位,如下所示:

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

但这仍然被视为 uint64_t,而不是双精度。这样做是未定义的行为,所以你应该确保它在你选择的编译器上按照你期望的方式工作,但是如果你把这个 uint64_t 放在一个联合中,然后读取联合的另一个双重成员,那么你会(希望!)将这些相同的位解释为由随机位组成的双精度位。

于 2013-04-24T03:08:27.470 回答
1

取决于您的平台,但假设 IEEE 754,例如Wikipedia,为什么不明确处理内部双格式?

(除非出现错误),这会生成随机但有效的双打。[这里还没有完全涵盖所有的基础,例如 case where exp = 0or 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;
}
于 2013-04-24T05:27:54.530 回答
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.

唯一需要将这些随机字节视为 adouble的地方是硬件返回结果的验证点。

此时,您需要查看字节并查看它们是否代表有效值。如果他们这样做,您可以memcpy()将字节放入 adouble并使用它。

下一个要处理的问题是上溢/下溢和异常,这些都是您需要对这些随机doubles数(加法、乘法等)执行的任何操作。您需要弄清楚如何在您的平台(编译器+CPU+OS)上处理它们,您是否可以安全可靠地检测到它们。

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

于 2013-04-24T05:40:56.403 回答