在0.0和1.0之间有多less个双数?

这是多年来一直在我心中的事情,但我从来没有花时间去问。

许多(伪)随机数发生器产生0.0到1.0之间的随机数。 在math上,在这个范围内有无限数字,但double是一个浮点数,因此具有有限的精度。

所以问题是:

  1. 在0.0和1.0之间有多less个double数?
  2. 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 ),你将拥有double2**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 …给或采取一个或两个;-)。

表示在0x00000000000000000x3ff0000000000000之间的每个double 0x3ff0000000000000位于区间[ 0x3ff0000000000000 ]中。 这是(2 ^ 62 – 2 ^ 52)不同的值(加上或减去一对夫妇,取决于你是否算端点)。

间隔[ 0x3ff0000000000000 ]对应于0x3ff00000000000000x400000000000000之间的表示; 这是2 ^ 52个不同的值。

间隔[ 0x4059000000000000 ]对应于0x40590000000000000x4059400000000000之间的表示; 这是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或最后一个单位。

  1. 2 ^ 53 – 包含隐藏位的64位浮点数的有效位数/尾数的大小。
  2. 大致是,因为sifnificand是固定的,但指数变化。

请参阅维基百科文章以获取更多信息。

Java double是一个IEEE 754 binary64号码。

这意味着我们需要考虑:

  1. 尾数是52位
  2. 指数是有1023个偏差的11位数字(也就是1023)
  3. 如果指数全部为0,尾数非零,则说明该数字是非归一化的

这基本上意味着总共有2 ^ 62-2 ^ 52 + 1个可能的双重表示,按标准在0到1之间。注意2 ^ 52 + 1是去除非规范化的情况数字。

请记住,如果尾数是正数,但指数是负数是正数,但小于1 🙂

对于其他数字,这有点难度,因为边缘整数在IEEE 754表示中可能不能精确地表示,而且由于指数中还有其他位可以表示数字,因此数字越小不同的价值观。