在 go 中生成给定范围内的随机 128 位十进制



假设我们有一个随机数生成器,可以生成随机的 32 或 64 位整数(如 rand。兰德在标准库中)

在给定范围内生成随机 int64[a,b]相当容易:

rand.Seed(time.Now().UnixNano())
n := rand.Int63n(b-a) + a

是否可以从 32 位或 64 位随机整数的组合在给定范围内生成随机 128 位十进制(如规范 IEEE 754-2008 中所定义)?

这是可能的,但解决方案远非微不足道。对于正确的解决方案,需要考虑几件事。

首先,具有指数E的值比具有指数E - 1的值的可能性高 10 倍。

其他问题包括跨零的次正规数字和范围。

我知道 Rademacher 浮点库,它解决了二进制浮点数的这个问题,但那里的解决方案很复杂,它的作者还没有写出他的算法是如何工作的。

编辑(5月11日):

我现在已经指定了一种生成随机"均匀"浮点数的算法——

  • 在任何范围内,
  • 全面覆盖,以及
  • 无论数字基数如何(例如二进制或十进制)。

可能,但绝非易事。下面是一个可能可接受的解决方案的草图——编写和调试它可能至少需要一天的共同努力。

minmax是原始的。十进制 128 来自 go.mongodb.org/mongo-driver/bson 的对象。设MAXBITS是 32 的倍数;128 可能就足够了。

  1. 获取有效数(尽可能大。Int) 和使用BigInt方法的minmax的指数(作为 int)。

  2. 对齐最小值和最大值,以便它们具有相同的指数。尽可能通过减小其指数并在其有效数的右侧添加相应数量的零来左对齐具有较大指数的值。如果这会导致有效数的绝对值变为>=2**(MAXBITS-1),则

    • (a) 通过从其有效数的右侧删除数字并增加其指数来右移具有较小指数的值,从而导致精度损失。
    • (b) 动态增加MAXBITS
    • (c) 抛出错误。
  3. 此时,两个
  4. 指数将相同,并且两个有效位数将是对齐的大整数。暂时搁置指数,让range(新big.Int)maxSignificand - minSignificand。它将介于 0 和2**MAXBITS之间。

  5. 使用BytesDivMod方法将range变成MAXBITS/32uint32,无论哪种方法更容易。

  6. 如果range的最高字等于math.MaxUint32则设置一个标志limitfalse,否则true

  7. 对于从 0 到MAXBITS/32的 n:

    • 如果limit为 true,则使用rand.Int63n(!,而不是rand.Int31nrand.Uint32)生成一个介于 0 和range的第 n 个单词(包括 0 和 n个单词)之间的值,将其强制转换为uint32,并将其存储为输出的第 n 个单词。如果生成的值等于range的第n个单词(即,如果我们为该单词生成了最大可能的随机值),则limit保持为true,否则将其设置为false。
    • 如果limit为 false,则使用rand.Uint32生成输出的第 n 个单词。 无论生成的值如何,limit都保持为假。
  8. 通过构建[]byte并使用big/Int.SetBytes或乘法和加法(如果方便)将生成的单词组合成big.Int

  9. 将生成的值添加到minSignificand以获得结果的有效数。

  10. ParseDecimal128FromBigInt与结果有效数和步骤 2-3 中的指数一起使用以获取结果。

该算法的核心是步骤 6,它一次生成任意长度 32 位的统一随机无符号整数。步骤 2 中的对齐将问题从浮点数减少到整数,步骤 3 中的减法将其减少到无符号,因此我们只需要考虑一个边界而不是 2。limit标志记录我们是否仍在处理该边界,或者我们是否已将结果缩小到不包含它的区间。

警告:

  1. 我没有写过这个,更不用说测试它了。我可能弄错了。欢迎由比我做更多数值计算工作的人进行健全性检查。
  2. 除非使用大得离谱的
  3. MAXBITS,否则在较大的动态范围内(包括交叉零)生成数字将失去一些精度,并省略一些可能具有较小指数的输出值;但是,128 位的结果应该至少与以十进制 128 实现的朴素算法一样好。
  4. 性能可能很差。

Go 有一个大数字包,可以做任意长度的整数:https://golang.org/pkg/math/big/

它有一个伪随机数生成器 https://golang.org/pkg/math/big/#Int.Rand,加密包也有 https://golang.org/pkg/crypto/rand/#Int

您希望使用 https://golang.org/pkg/math/big/#Int.Exp 指定最大值作为2^128

但是,不能说性能,或者这是否符合IEEE标准,但是像用于UUID的大型随机数是可能的。

这取决于您要生成多少个值。如果在指定范围内不再有 10^34 个值就足够了 - 这很简单。

正如我看到的问题,最小范围内的随机值。最大值可以计算为随机(0..1)*(最大-最小)+最小值

看起来我们只需要在 0..1 范围内生成十进制 128 的值。所以它是一个随机值,范围为 0..10^34-1,指数为 -34。可以使用 golang 标准随机包生成此值。

要相乘,可以使用 golang 数学/大包进行值规范化,添加和构造 float128 值。

这绝对是你要找的。

最新更新