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

fel*_*lix 5 random go

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

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

rand.Seed(time.Now().UnixNano())
n := rand.Int63n(b-a) + a
Run Code Online (Sandbox Code Playgroud)

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

hob*_*bbs 1

有可能,但绝非易事。这是一个可能可以接受的解决方案的草图 \xe2\x80\x94 编写和调试它可能至少需要一天的共同努力。

\n\n

minmax为来自 go.mongodb.org/mongo-driver/bson 的Primitive.Decimal128对象。设MAXBITS为 32 的倍数;128 可能就足够了。

\n\n
    \n
  1. 使用该方法获取minand的有效数(如 big.Int)和指数(如 int)。maxBigInt

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

    \n\n
      \n
    • (a) 通过删除其有效数右侧的数字并增加其指数来右移具有较小指数的值,从而导致精度损失。
    • \n
    • (b) 动态增加MAXBITS
    • \n
    • (c) 抛出错误。
    • \n
  4. \n
  5. 此时,两个指数将相同,并且两个尾数将与大整数对齐。现在先把指数放在一边,让range(一个新的big.Int)为maxSignificand - minSignificand。它将在 0 和 之间2**MAXBITS

  6. \n
  7. 使用or方法将其转换range为s ,无论哪种方法都更容易。MAXBITS/32 uint32BytesDivMod

  8. \n
  9. 如果 的最高字range等于 ,则设置math.MaxUint32一个标志,否则。limitfalsetrue

  10. \n
  11. 对于 n 从 0 到MAXBITS/32

    \n\n
      \n
    • 如果limit为 true,则使用rand.Int63n(!, not rand.Int31n or rand.Uint32) 生成一个介于 0 和 的第 n 个字(range含)之间的值,将其转换为uint32,并将其存储为输出的第 n 个字。如果生成的值等于第 n 个单词range(即,如果我们为该单词生成了最大可能的随机值),则保持limittrue,否则设置为 false。
    • \n
    • 如果limit为 false,则用于rand.Uint32生成输出的第 n 个字。limit无论生成的值如何,都保持为 false。
    • \n
  12. \n
  13. big.Int如果方便的话,可以通过构建 a[]byte和 使用big/Int.SetBytes乘法和加法将生成的单词组合成 a 。

  14. \n
  15. 将生成的值相加以minSignificand获得结果的有效数。

  16. \n
  17. 与步骤 2-3 中的结果有效数和指数一起使用ParseDecimal128FromBigInt以获得结果。

  18. \n
\n\n

该算法的核心是步骤 6,它一次生成任意长度 32 位的均匀随机无符号整数。第 2 步中的对齐将问题从浮点数简化为整数 1,第 3 步中的减法将其简化为无符号1,这样我们只需考虑 1 界限而不是 2。标志limit记录了我们是否仍在处理该界限,或者我们是否已经将结果缩小到不包含它的区间。

\n\n

注意事项:

\n\n
    \n
  1. 我还没有写过这个,更不用说测试了。我可能完全错了。欢迎比我进行更多数值计算工作的人进行健全性检查。
  2. \n
  3. 生成大动态范围(包括过零)的数字将损失一些精度,并忽略一些可能的具有较小指数的输出值,除非MAXBITS使用大得可笑的指数;然而,128 位应该给出的结果至少与以十进制 128 实现的简单算法一样好。
  4. \n
  5. 性能可能很差。
  6. \n
\n