将随机字节转换为.NET十进制0..1小数范围

时间:2016-09-03 07:44:36

标签: c# random

我正在使用TRNG usb设备,并成功将随机字节转换为C#中的各种可用数据。

我想使用二进制阅读器(或其他直接字节 - >十进制)方法直接从字节创建介于0..1(例如:0.37327)之间的.NET十进制值。

// assume: byte[] random_data of appropriate length, patterned for a 0..1 range decimal value = new BinaryReader(new MemoryStream(random_data)).ReadDecimal();

我一直在寻找十进制的字节格式,但看起来它可能不是标准?

这是为了爱好工作,所以我可以接受将来可能会改变的东西。 我查看了为示例输入十进制值生成的字节 - 我看到了负数和精度标志(最后一个int32中的1Ch最大值?),但是转储编译器常量的最小/最大数据值让我感到有点困难,我生成了高于零值或无效值:

  • 最接近1的分数值(.99 ... 9):FFFFFF0F 6102253E 5ECE4F20 00001C00
  • 小数值最接近0(.00 ... 1):01000000 00000000 00000000 00001C00

你能帮助我找到产生完整小数0..1范围的正确路径吗?

使用“最终”代码进行编辑,感谢所有人!

这是我最终用来从合适的随机字节流(如TRNG设备,www.Random.org或CSPRNG算法)创建范围[0..1]的无偏随机小数的C#代码。生成的值对于眼球看起来很好,边界测试通过,并且只要我避免使用拼写错误和复制/粘贴错误,这应该可以原样使用。

感谢您的帮助和有趣的讨论!

private decimal RandomDecimalRange01()
{
    // 96 bits of random data; we'll use 94 bits to directly map decimal's max precision 0..1 range
    byte[] data = new byte[12];
    decimal value = 0;

    // loop until valid value is generated, discarding invalids values. Mostly controlled by top 2 bits: 11 is always invalid, 00 or 01, is always valid, 10 has valid and invalid ranges. Odds make loop generally find value in one or a few iterations.
    while (true)
    {
        // Acquire random bytes from random source (like TRNG device or CSPRNG api)
        if (!trng.GetBytes(data))
        {
            throw new Exception("Failed to aquire random bytes from source");
        }
        else
        {
            // Read 94 random bits (pull 96 bits, discard 2)
            BinaryReader reader = new BinaryReader(new MemoryStream(data));
            int low = reader.ReadInt32();
            int mid = reader.ReadInt32();
            int high = reader.ReadInt32() & 0x3FFFFFFF; // don't consume upper 2 random bits - out of range

            // Discard invalid values and reloop (interpret special invalid value as 1)
            if (high > 542101086)
            {
                continue;
            }
            else if (high == 542101086)
            {
                if (mid > 1042612833)
                {
                    continue;
                }
                else if (mid == 1042612833)
                {
                    if (low > 268435455)
                    {
                        // Special override to generate 1 value for inclusive [0..1] range - interpret the smallest invalid value as 1. Remove code for exclusive range [0..1)
                        if (low == 268435456)
                        {
                            value = 1m; // return 1.0
                            break;
                        }
                        continue;
                    }
                }
            }

            // return random decimal created from parts - positive, maximum precision 28 (1C) scale
            value = new decimal(low, mid, high, false, 28);
            break;
        }
    }
    return value;
}

通过算法

运行TrueRNGPro TRNG设备字节的示例生成值
0.8086691474438979082567747041
0.4268035919422123276460607186
0.7758625805098585303332549015
0.0701321080502462116399370731
0.3127190777525873850928167447
0.6022236739048965325585049764
0.1244605652187291191393036867

测试有趣的边界值

// test databyte values for max & min ranges
new byte[] { 0x01, 0x00, 0x00, 0x10, 0x61, 0x02, 0x25, 0x3E, 0x5E, 0xCE, 0x4F, 0x20 }; // boundary: 1 too large for algorithm, will be discarded
new byte[] { 0x00, 0x00, 0x00, 0x10, 0x61, 0x02, 0x25, 0x3E, 0x5E, 0xCE, 0x4F, 0x20 }; // boundary: special 1 more than largest valid .99999..., interpret as 1 value
new byte[] { 0xFF, 0xFF, 0xFF, 0x0F, 0x61, 0x02, 0x25, 0x3E, 0x5E, 0xCE, 0x4F, 0x20 }; // boundary: largest valid value .9999...
new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // boundary: smallest valid value, should be 0
new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // boundary: 1 more than smallest valid value, should be .000...1

2 个答案:

答案 0 :(得分:0)

来自https://msdn.microsoft.com/en-us/library/system.decimal.getbits(v=vs.110).aspx

  

十进制数的二进制表示由1位组成   符号,96位整数,以及用于分割的缩放因子   整数,并指定它的小数部分。   缩放因子隐含地为数字10,提升为指数   范围从0到28。

     

返回值是一个由32位有符号整数组成的四元素数组。

     

返回数组的第一个,第二个和第三个元素包含   96位整数的低,中,高32位。

     

返回数组的第四个元素包含比例因子和   标志。它由以下部分组成:

     

0到15位,低位字未使用,必须为零。

     

第16位至第23位必须包含0到28之间的指数   表示10的幂除以整数。

     

第24至30位未使用且必须为零。

     

位31包含符号:0表示正数,1表示负数。

您正在寻找0< = 96bitinteger / 10 指数< = 1

乘以,这与0< = 96bitinteger< = 10 ^ exponent

相同

这将枚举每个可能的96bitinteger和exponent对,它们将产生0到1之间的decimal值(假设符号位设置为0)。

for (int exponent=0; exponent<=28; exponent++) {
    BigInteger max = BigInteger.Pow(10, exponent);
    for (int i = 0; i <= max; i++) {
        var fmt = "96bitinteger: {0}, exponent: {1}";
        Console.WriteLine(String.Format(fmt, i, exponent));
    }
}

使用指数= 28

十六进制中的

10 28 204fce5e 3e250261 10000000。所以一旦你根据文档放置32位数字,然后创建最后的32位,因为某些原因,当他们说第0位意味着最高位时,事实,从字节构造十进制数1太难了。

int[] data = new int[] { 0x10000000, 0x3e250261, 0x204fce5e, 0x1C0000 };
var random_data = data.SelectMany(BitConverter.GetBytes).ToArray();
decimal value = new BinaryReader(new MemoryStream(random_data)).ReadDecimal();
Console.WriteLine(value);

考虑更一般的new int[] { a, b, c, 0x1C0000 }。对于创建十进制数0&lt; = d&lt; = 1的b和c的约束是

if c < 0x204fce5e:
    a can be anything
    b can be anything
elif c = 0x204fce5e:
    if b < 0x3e250261:
        a can be anything
    elif b = 0x3e250261
        constrain a <= 10000000
    b can not be greater than 0x3e250261
c can not be greater than 0x204fce5e.

答案 1 :(得分:0)

  • 你想在0和1(包括两端)之间进行均匀分配吗?
  • 原始数据在字节级是否也是统一的?
  • 你真的需要多少精确度?
  • 你需要小数或浮点数/双倍吗?

如果你使用十进制,我不认为你会得到统一的分布,因为你不能使用所有字节的整个范围。

对于浮点数,我认为如果你创建一个介于1和2之间的数字然后减去1来进行位操作可能会更容易。但是,你永远不会得到1.0。