就像this question的质问者一样,我想知道为什么Math.ceil(Math.random() * 10)
不比Math.floor(Math.random() * 10) + 1
更受青睐,并且发现这是因为Math.random的机会很小(但相关)确切地返回0。但是有多小?
进一步的研究告诉我,this random number is accurate to 16 decimal places... well, sort of.是我很好奇的“一种”。
我知道浮点数的工作方式不同于小数。但是我在细节上挣扎。如果数字是严格的十进制值,我相信机率是十分之一(在美国系统中为十万亿)-1:10 16 。
这是正确的,还是我搞砸了,还是浮点数有所作为?
答案 0 :(得分:2)
JavaScript是ECMAScript的方言。 ECMAScript-262 standard无法准确指定Math.random
。在第20.2.2.7条中,它说:
20.2.2.27 Math.random()
使用依赖于实现的算法或策略,返回一个正号(大于或等于0但小于1)的Number值,该值是在该范围内以近似均匀的分布随机或伪随机选择的。此函数不带参数。 为不同领域创建的每个 Math.random 函数必须从连续调用中产生不同的值序列。
在没有完整规范的情况下,无法就Math.random
返回零的概率做出明确的陈述。每个ECMAScript实现都可以选择不同的算法,而不必提供真正统一的分布。
ECMAScript的Number
类型使用IEEE-754基本的64位二进制浮点格式。在此格式中,数字的有效位数(小数部分)具有53位。每个浮点数的格式为 s • f •2 e ,其中 s (用于符号)是+1或-1, f (用于分数)是有效位数,并且是[0,2 53 ]中的整数,而 e (对于指数)是[−1074,971]中的整数。如果设置了 f 的高位,则该数字被归一化(因此 f 位于[2 52 ,2 53 ))。由于在此答案中无需考虑负数,因此让 s 对该答案的其余部分隐式+1。
在[0,1)中分配随机数的一个问题是可代表的值没有均匀分布。在[½,1)中有2个 52 可表示的值-所有在[2 52 ,2 53 )和 e = −53。并且[¼,½)中的值数量相同-所有在[2 52 ,2 53 )和< em> e = −54。由于在此间隔中有相同数量的数字,但间隔是长度的一半,因此数字之间的间隔更近。同样,在[⅛,¼)中,间距再次减半。这一直持续到指数达到−1074为止,此时正常数以 f = 2 52 结尾。小于那个的数被认为是次正规的(或零),其中 f 在[0,2 52 )中,而 e = −1074 ,并且它们之间的间距均匀。
关于如何分配Math.random
的数字的一种选择是仅使用一组均匀间隔的数字 f •2 −53 用于 [0,2 53 )中的f 。这将使用[1/2,1)中所有可表示的值,但仅使用[1/4,½)中的一半,[1/3,¼)中的四分之一,依此类推。这很简单,并且避免了分布中的一些奇怪之处。如果正确实施,则产生零的概率为2 53 中的一。
另一种选择是使用[0,1)中的所有可表示值,每个值的概率与从它到下一个更高的可表示值的距离成比例。因此,将以概率1/2 53 选择[1/2,1)中的每个可表示数字,以概率<1/2 54 < / sup>,则以概率1/2 55 选择[⅛,¼)中每个可表示的数字,依此类推。在浮点格式更精细的情况下,此分布近似于实数和提供者更精确的分布。如果正确实施,则产生零的概率为2 1074 中的一。
另一种选择是使用[0,1)中的所有可表示值,每个值与段长度成正比,其中可表示值是该段中所有实数的最接近可表示值。我将省略对这种分布的一些细节的讨论,只是说它模仿一个结果,方法是选择一个具有均匀分布的实数,然后使用“四舍五入关系”将其四舍五入为可表示的值。 。如果正确实施,则产生零的概率为2 1075 之一。 (这种分布的一个问题是,在[0,1]上的实数上的均匀分布有时会产生一个非常接近1的数字,从而使舍入产生1。这然后要求允许Math.random
返回1或通过某种方式弄乱了分布,也许是通过返回下一个较低的可表示值而不是1。)
我将注意到ECMAScript规范过于宽松,以至于人们可能会断言Math.random
可能会为每个可表示的值以相等的概率分配数字,而忽略它们之间的间隔。这根本不会模仿真实数字的均匀分布,我希望很少有人会喜欢它。但是,如果实施,则返回零的概率为1021•2 52 中的1,因为存在2 52 归一化的数字,其指数从-53到-1074(1020值 e )和2 52 次正规或零数。