看起来Math.random()生成[0,1]范围内的64位浮点数,而新的crypto.getRandomValues()API只返回整数。使用此API在[0,1)中生成数字的理想方法是什么?
这似乎有效,但似乎不是最理想的:
ints = new Uint32Array(2)
window.crypto.getRandomValues(ints)
return ints[0] / 0xffffffff * ints[1] / 0xffffffff
编辑:为了澄清,我试图产生更好的结果而不是Math.random()。根据我对浮点的理解,应该可以获得52位随机性的完全随机分数。 (?)
编辑2:为了提供更多背景知识,我不是要尝试以密码方式安全做任何事情,但有很多关于Math.random()的轶事故事实施得很差(例如http://devoluk.com/google-chrome-math-random-issue.html)所以其中我可以使用更好的替代品。答案 0 :(得分:7)
请记住,浮点数只是一个尾数系数,乘以2加到指数:
floating_point_value = mantissa * (2 ^ exponent)
使用Math.random
,您生成具有32位随机尾数的浮点,并且始终具有指数-32
,以便小数位移位到左边32个位置,所以尾数永远不会有小数点左边的任何部分。
mantissa = 10011000111100111111101000110001 (some random 32-bit int)
mantissa * 2^-32 = 0.10011000111100111111101000110001
尝试运行Math.random().toString(2)
几次以验证是否属实。
解决方案:您只需生成一个随机的32位尾数并将其乘以Math.pow(2,-32)
:
var arr = new Uint32Array(1);
crypto.getRandomValues(arr);
var result = arr[0] * Math.pow(2,-32);
// or just arr[0] * (0xffffffff + 1);
注意浮点没有均匀分布(由于尾数精度不高,数字变得越大,可能的值越稀疏),使得它们不适合加密应用程序或其他需要非常强大的随机数的域。为此,您应该使用crypto.getRandomValues()
提供给您的原始整数值。
修改强>
JavaScript中的尾数为52位,因此您可以获得52位随机性:
var arr = new Uint32Array(2);
crypto.getRandomValues(arr);
// keep all 32 bits of the the first, top 20 of the second for 52 random bits
var mantissa = (arr[0] * Math.pow(2,20)) + (arr[1] >>> 12)
// shift all 52 bits to the right of the decimal point
var result = mantissa * Math.pow(2,-52);
所以,总而言之,不,这不比你自己的解决方案短,但我认为这是你希望做的最好的。您必须生成52个随机位,这些位需要从32位块构建,然后需要将其向下移回到1以下。
答案 1 :(得分:0)
嗯,如果您确实需要[0,1)范围内的数字,那就是最佳状态。
该代码的问题在于不同数字的几率不再相同。
使用该代码更可能获得0.5(1 * 0.5,0.5 * 1,0.75 * 0.666)而不是1(1 * 1)。
答案 2 :(得分:0)
bit-twiddling version in this duplicate也很好,因为the spec says that numbers are IEEE 754。另外,请考虑DataView对endianness的评论。