将随机字节转换为.net十进制0.1小数范围
本文关键字:小数 范围 十进制 net 随机 字节 转换 | 更新日期: 2023-09-27 18:05:22
我正在玩TRNG usb设备,并成功地将随机字节转换为c#中的各种可用数据。
我想创建一个介于0..1之间的。net十进制值(例如:0.37327)直接从字节使用二进制读取器(或其他直接字节->十进制)的方法。
// assume: byte[] random_data of appropriate length, patterned for a 0..1 range
decimal value = new BinaryReader(new MemoryStream(random_data)).ReadDecimal();
我正在寻找十进制的字节格式,但它看起来可能不是一个标准?
- 如何将字节值转换为小数?
- .NET十进制跨平台标准
这是我的业余爱好,所以我可以接受使用一些将来可能会改变的东西。我已经查看了为示例输入十进制值生成的字节-我看到了负和精确标志(最后int32中的1Ch max ?),但是最小/最大数据值转储编译器常量让我有点难倒,并且我生成的值高于零或无效值:
- 最接近1的分数值(0.99…9):FFFFFF0F 6102253E 5ECE4F20 00001C00
- 最接近0(.00…1)的小数值:01000000 00000000 00000000 00001C00
你能帮我找到生成完整分数0的正确路径吗?1范围?
编辑"最终"代码,感谢大家!
这是我最终创建一个范围为[0..]的无偏随机小数的c#代码。1]从合适的随机字节流(如TRNG设备,www.Random.org或CSPRNG算法)。生成的值看起来不错,边界测试通过了,只要我避免了令人尴尬的打字错误和复制/粘贴错误,这应该是可用的。
谢谢你的帮助和有趣的讨论!
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
From https://msdn.microsoft.com/en-us/library/system.decimal.getbits(v=vs.110).aspx
十进制数的二进制表示由1位组成符号,一个96位的整数,和一个用于除的比例因子整数,并指定它的哪一部分是小数。比例因子是隐含的数字10,取一个指数取值范围0 ~ 28。
返回值是一个包含4个元素的32位有符号整数数组。
返回数组的第一个、第二个和第三个元素包含96位整数的低、中、高32位。
返回数组的第四个元素包含比例因子和的迹象。它由以下部分组成:
第0位到第15位是未使用的,必须为零。
16 ~ 23位必须包含0 ~ 28之间的指数整数除以10的幂。
24 ~ 30位未使用,必须为零。
第31位包含符号:0表示正,1表示负。
您正在寻找0 <= 96bitinteger/10指数 <= 1
将其相乘,就等于0 <= 96bitinteger <= 10^exponent
这将枚举产生decimal
值在0到1之间的所有可能的96位整数和指数对(假设符号位设置为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
1028为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 <= d <= 1, a、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.
- 您是否希望0和1之间均匀分布(包括两端)?
- 原始数据在字节级别也是统一的吗?
- 你真正需要多少精度?
- 你需要十进制还是浮点/双精度?
如果你使用十进制,我不认为你会得到均匀分布,因为你不能使用所有字节的整个范围。
对于浮点数,我认为如果你在1和2之间创建一个数字,然后减去1,那么做位操作可能会更容易。但是,您永远不会得到完全的1.0。