在0.0和1.0之间有多less个双数?
这是多年来一直在我心中的事情,但我从来没有花时间去问。
许多(伪)随机数发生器产生0.0到1.0之间的随机数。 在math上,在这个范围内有无限数字,但double
是一个浮点数,因此具有有限的精度。
所以问题是:
- 在0.0和1.0之间有多less个
double
数? - 1和2之间的数字是否一样多? 在100和101之间? 在10 ^ 100和10 ^ 100 + 1之间?
注意:如果它有所作为,我特别感兴趣的是Java的double
定义。
Java double
s采用IEEE-754格式,因此它们有52位分数; 在两个相邻的两个权力之间(包括一个和不包括下一个权力),因此将有2到52个不同的double
(即它们的4503599627370496)。 例如,这是包含0.5和1.0之间的不同double
数的数目,正好也包含1.0和2.0之间的数目,依此类推。
在0.0到1.0之间计算doubles
比在两个幂之间这样做要困难得多,因为在这个范围内包含了很多的两个幂,而且还有一个进入非规范化数的棘手问题。 指数的11位中有10位涵盖了所讨论的范围,因此,包括非规范化的数字(我认为有几种NaN
),你将拥有double
于2**62
幂数的double
的1024倍 – 不超过2**62
总共有2**62
。 不包括非规范化的&c,我相信计数将是1023次2**52
。
对于像“100到100.1”这样的任意范围来说,它更难,因为上限不能被精确地表示为double
精度(不是两个幂的精确倍数)。 作为一个方便的近似值,因为两个幂的线性关系是线性的,所以可以说这个范围是两个(64和128)的幂间的跨度的0.1 / 64
,所以你可以期望
(0.1 / 64) * 2**52
独特的double
s – 来到7036874417766.4004
…给或采取一个或两个;-)。
表示在0x0000000000000000
和0x3ff0000000000000
之间的每个double
0x3ff0000000000000
位于区间[ 0x3ff0000000000000
]中。 这是(2 ^ 62 – 2 ^ 52)不同的值(加上或减去一对夫妇,取决于你是否算端点)。
间隔[ 0x3ff0000000000000
]对应于0x3ff0000000000000
和0x400000000000000
之间的表示; 这是2 ^ 52个不同的值。
间隔[ 0x4059000000000000
]对应于0x4059000000000000
和0x4059400000000000
之间的表示; 这是2 ^ 46个不同的值。
10 ^ 100和10 ^ 100 + 1之间没有双打 。 这两个数字中的任何一个都不能以双精度表示,而且在它们之间没有双打。 最接近的两个双精度数字是:
99999999999999982163600188718701095...
和
10000000000000000159028911097599180...
其他人已经解释说,在范围[0.0,1.0]内大约有2 ^ 62双打。
(并不奇怪:几乎有2 ^ 64个有限双打,其中有一半是正数,大约一半是<1.0)。
但是你提到随机数发生器:注意一个随机数发生器产生0.0到1.0的数字通常不能产生所有这些数字; 通常它只会生成n / 2 ^ 53格式的数字,其中n是一个整数(参见例如nextDouble的Java文档)。 所以random()
输出通常只有2 ^ 53(+/- 1,取决于包含哪些端点)的可能值。 这意味着绝大多数[0.0,1.0]中的双精度将永远不会生成。
文章Java的新math,第2部分:来自IBM的浮点数提供了下面的代码片段来解决这个问题(在浮点数,但我怀疑它也适用于双打):
public class FloatCounter { public static void main(String[] args) { float x = 1.0F; int numFloats = 0; while (x <= 2.0) { numFloats++; System.out.println(x); x = Math.nextUp(x); } System.out.println(numFloats); } }
他们对此有这样的评论:
事实上,在1.0和2.0之间的确有8,388,609个浮点数; 很大但几乎不存在这个范围内存在的实数的无穷无穷。 连续数字大约相差0.0000001。 这个距离被称为最低精度单位的ULP或最后一个单位。
- 2 ^ 53 – 包含隐藏位的64位浮点数的有效位数/尾数的大小。
- 大致是,因为sifnificand是固定的,但指数变化。
请参阅维基百科文章以获取更多信息。
Java double是一个IEEE 754 binary64号码。
这意味着我们需要考虑:
- 尾数是52位
- 指数是有1023个偏差的11位数字(也就是1023)
- 如果指数全部为0,尾数非零,则说明该数字是非归一化的
这基本上意味着总共有2 ^ 62-2 ^ 52 + 1个可能的双重表示,按标准在0到1之间。注意2 ^ 52 + 1是去除非规范化的情况数字。
请记住,如果尾数是正数,但指数是负数是正数,但小于1 🙂
对于其他数字,这有点难度,因为边缘整数在IEEE 754表示中可能不能精确地表示,而且由于指数中还有其他位可以表示数字,因此数字越小不同的价值观。