C# 如何从随机字节数组值中获取随机双精度值?

C# 如何从随机字节数组值中获取随机双精度值?,c#,.net,random,C#,.net,Random,我想使用RNGCryptoServiceProvider作为随机数的来源。由于它只能将它们作为字节值数组输出,如何将它们转换为0到1的双倍值,同时保持结果的一致性?您可以使用BitConverter.ToDouble(…)方法。它接受一个字节数组并返回一个双精度。大多数其他基元类型都有相应的方法,以及从基元到字节数组的方法。使用位转换器将随机字节序列转换为双字节: byte[] random_bytes = new byte[8]; // BitConverter will expect an

我想使用RNGCryptoServiceProvider作为随机数的来源。由于它只能将它们作为字节值数组输出,如何将它们转换为0到1的双倍值,同时保持结果的一致性?

您可以使用BitConverter.ToDouble(…)方法。它接受一个字节数组并返回一个双精度。大多数其他基元类型都有相应的方法,以及从基元到字节数组的方法。

使用位转换器将随机字节序列转换为双字节:

byte[] random_bytes = new byte[8];  // BitConverter will expect an 8-byte array
new RNGCryptoServiceProvider().GetBytes(random_bytes);

double my_random_double = BitConverter.ToDouble(random_bytes, 0);

我就是这样做的

private static readonly System.Security.Cryptography.RNGCryptoServiceProvider _secureRng;
public static double NextSecureDouble()
{
  var bytes = new byte[8];
  _secureRng.GetBytes(bytes);
  var v = BitConverter.ToUInt64(bytes, 0);
  // We only use the 53-bits of integer precision available in a IEEE 754 64-bit double.
  // The result is a fraction, 
  // r = (0, 9007199254740991) / 9007199254740992 where 0 <= r && r < 1.
  v &= ((1UL << 53) - 1);
  var r = (double)v / (double)(1UL << 53);
  return r;
}

对于替身(比如NaN)来说,有些特殊情况是你想要避免的。-0 Jon是对的。更糟糕的是数字不在0和1之间。请尝试以下操作:BitConverter.GetBytes(double.NaN)。返回以248255结尾的8字节数组。请参阅我对Michael帖子的评论。可能会导致NaN、-无穷大等。谢谢,但结果将非常不均匀,其密度将增加到0,并且没有机会在0.5到1之间获得任何东西。我的大脑今天无法处理简单的数学。这正是我想做的+1给你。是的,这好多了。。。值为1.0的情况非常糟糕。。。在已经从1到0的一半之前,您只需要一个大于2的双精度值。2..double.Max的翻倍比0..2的翻倍多得多。谢谢你,Mehrdad!工作起来很有魅力!看起来这个解决方案可能(如果你真的(不)幸运的话)返回1。但是Random.NextDouble()返回一个介于0(包括0)和1(排除)之间的值。@bruce965第一个问题:这是因为
ulong.MaxValue
是分子中可以得到的最大值。第二个问题:不,因为分子总是正的(它是无符号的)。第三个问题:不知道你在说什么。。。
3
与任何事情都有什么关系
2^64
比double.MaxValue小得多,因此没有
无穷大
,它变成
NaN
的唯一方法是分母为零,而分母不是。编辑:也许你认为《双重密码》的演员阵容是闪电战?不是。我认为这应该是可以接受的答案,因为它与Random的行为相同,它从不返回1.0。如果我错了,请纠正我。。。你可以简化部分代码:
v&=((1UL>11)不是吗?@Yellowfive当然可以,但是
v&=((1UL
private static readonly System.Security.Cryptography.RNGCryptoServiceProvider _secureRng;
public static double NextSecureDouble()
{
  var bytes = new byte[8];
  _secureRng.GetBytes(bytes);
  var v = BitConverter.ToUInt64(bytes, 0);
  // We only use the 53-bits of integer precision available in a IEEE 754 64-bit double.
  // The result is a fraction, 
  // r = (0, 9007199254740991) / 9007199254740992 where 0 <= r && r < 1.
  v &= ((1UL << 53) - 1);
  var r = (double)v / (double)(1UL << 53);
  return r;
}
[Test]
public void Randomness_SecureDoubleTest()
{
  RunTrials(1000, 0.02);
  RunTrials(10000, 0.01);
}

private static void RunTrials(int sampleSize, double errorMargin)
{
  var q = new Queue<double>();

  while (q.Count < sampleSize)
  {
    q.Enqueue(Randomness.NextSecureDouble());
  }

  for (int k = 0; k < 1000; k++)
  {
    // rotate
    q.Dequeue();
    q.Enqueue(Randomness.NextSecureDouble());

    var avg = q.Average();

    // Dividing by n−1 gives a better estimate of the population standard
    // deviation for the larger parent population than dividing by n, 
    // which gives a result which is correct for the sample only.

    var actual = Math.Sqrt(q.Sum(x => (x - avg) * (x - avg)) / (q.Count - 1));

    // see http://stats.stackexchange.com/a/1014/4576

    var expected = (q.Max() - q.Min()) / Math.Sqrt(12);

    Assert.AreEqual(expected, actual, errorMargin);
  }
}