0.0和1.0之间有多少个双重数字?

104

这是我多年来一直在思考的问题,但以前从未花时间询问过。

许多(伪)随机数生成器生成0.0到1.0之间的随机数。从数学上讲,在此范围内有无限多的数字,但double是浮点数,因此具有有限的精度。

所以问题是:

  1. 在0.0和1.0之间有多少个double数字?
  2. 在1和2之间有同样多的数字吗?在100和101之间?在10^100和10^100+1之间?

注意:如果有差异,我特别关注Java对double的定义。

6个回答

70
Java的双精度数采用IEEE-754格式,因此它们具有52位小数;在任何两个相邻的二次幂之间(包括一个而不包括下一个),因此将有2的52次方个不同的双精度数(即4503599627370496个)。例如,在0.5到1.0之间有不同的双精度数,恰好也有这么多在1.0到2.0之间,依此类推。
计算0.0到1.0之间的双精度数比在二次幂之间进行更困难,因为该范围内包含许多二次幂,并且还涉及到非规格化数的棘手问题。指数的11位中的10位覆盖了所讨论的范围,因此,包括非规格化数(我认为还包括一些NaN类型),您将获得1024倍于二次幂之间的双精度数的总数,最多不超过2的62次方个。排除非规格化等情况,我认为计数将为1023倍的2的52次方。

对于类似"100到100.1"这样的任意范围,情况更加复杂,因为上限不能被表示为一个准确的double(不是2的任何幂的准确倍数)。作为一个便捷的近似,由于两个幂次之间的进展是线性的,你可以说该范围是包围两个幂次之间跨度的0.1 / 64,所以你应该期望大约

(0.1 / 64) * 2**52

distinct doubles -- 这大约是 7036874417766.4004,误差在一两个左右 ;-).


5
让我确认一下:因为double类型是64位的,所以可能的double值不能超过2的64次方,而似乎相当大比例的这些值在0到1之间? - polygenelubricants
11
@polygene,是的,而且是的——具体来说,对于任何“正常”的浮点表示法(基数、指数和分数长度),可能值的大约四分之一位于0.0和1.0之间(另外四分之一位于1.0和无穷大之间,其余一半位于实轴负半部分)。本质上,指数的一半值(带有正常偏差,在其范围内的中点)表示底数的负幂,因此小于1.0的数字。 - Alex Martelli
9
在许多应用中,0和1之间的范围比100和101之间的范围更加重要和有趣,因此它获得了更大比例的值。例如,在物理学中,你经常需要处理像牛顿万有引力常数6.67e-11这样极小的值。在那里拥有良好的精度比在100和101之间更有用。请阅读http://floating-point-gui.de/以获取更多信息。 - Michael Borgwardt
1
您还可以将任何数字缩放到0.0和1.0之间,并单独跟踪比例,从而在计算中产生更少的误差。当整个数线可以映射在两个数字之间时,这是非常好的! - codekaizen
如果在0和0.5之间有1023 * 2 ^ 52个双精度浮点数,但在0.5和1之间只有2 ^ 52个双精度浮点数,那么根据鸽笼原理,返回介于0和1之间的随机双精度浮点数的函数是否会不平衡? - dimo414
显示剩余5条评论

50

所有double类型值的表示范围在0x00000000000000000x3ff0000000000000之间,位于区间[0.0, 1.0]内。这是2^62 - 2^52个不同的值(加上或减去一些端点取决于是否计算端点)。

区间[1.0, 2.0]对应的表示范围在0x3ff00000000000000x400000000000000之间;这是2^52个不同的值。

区间[100.0, 101.0]对应的表示范围在0x40590000000000000x4059400000000000之间;这是2^46个不同的值。

10^100和10^100 + 1之间没有任何double类型的值。这两个数字都无法在双精度浮点数中表示,并且没有双精度浮点数落在它们之间。最接近的两个双精度浮点数是:

99999999999999982163600188718701095...
< p>and < /p> 和
10000000000000000159028911097599180...

对于一个得到良好支持的精确答案,点赞+1。(如果你在计算端点时很挑剔,请记住+0.0和-0.0具有不同的表示形式。) - Jim Lewis
1
+1,这个结局太出乎意料了!感觉就像在读M·奈特·沙马兰的剧本! - polygenelubricants

9
其他人已经解释过,在范围 [0.0, 1.0] 中大约有 2^62 个双精度浮点数。
(这并不令人惊讶:几乎有 2^64 个不同的有限双精度浮点数;其中一半是正数,大约一半小于 1.0。)

但是你提到了随机数生成器:请注意,生成介于 0.0 和 1.0 之间的随机数的随机数生成器通常 不能 产生所有这些数字;通常它只能产生形如 n/2^53 的数字,其中 n 是整数(例如,请参见 Java 文档中的 nextDouble)。因此,random() 输出的可能值通常只有大约 2^53 (+/-1,具体取决于包含哪些端点)。这意味着在 [0.0,1.0] 中的大多数双精度浮点数将永远不会被生成。


3

IBM的文章Java的新数学,第2部分:浮点数提供以下代码片段来解决此问题(使用浮点数,但我认为它也适用于双精度):

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,即最小精度单元或最后一位单位。


是的,但这是针对float而言,不是 double——float有23位小数,因此在相邻两个2的幂之间有8388608个不同的值(当然,“包含”部分意味着您必须再计算一个,即下一个2的幂)。double有52位小数! - Alex Martelli
2
@Alex:我想在程序(修改为双精度)运行到宇宙末日之前,我都得等到结果出来... :( - Mark Rushakoff
1
我感觉很蠢;我刚刚写了“double”的等价物,然后想:“嘿,我将在大约5分钟内回答自己的问题...” - polygenelubricants
1
@polygene:这感觉像是一个Project Euler问题,明显的方法无法计算,但一定有一些非常简单的公式可以解决任意情况... - Mark Rushakoff
2
也许不需要真正的超级计算机:在一个只需一纳秒运行内部循环的机器上,使用double在相邻的2的幂之间进行计数大约需要52天(当然,无论如何,println都不太可能以任何速度运行,所以让我们假设这个语句消失了;-)。我认为,在一台强大但现实的机器上,需要一年或更短的时间就可以完成。;-) - Alex Martelli

2
  1. 2的53次方 - 64位浮点数的尾数/有效数字大小,包括隐藏位。
  2. 大致上是这样的,因为尾数是固定的,但指数会变化。

更多信息请参见维基百科文章


你对第二个问题的回答与我理解的FP工作方式相矛盾。 - polygenelubricants
我认为1是错误的,因为隐藏位始终为一--因此,有2^52而不是2^53个不同的值(在相邻的两个二次幂之间,一个包括在内,下一个排除在外--而不是在0.0和1.0之间!)。 - Alex Martelli

1

Java中的double是IEEE 754二进制64位数字。

这意味着我们需要考虑:

  1. 尾数为52位
  2. 指数为11位数字,带有1023偏差(即加上1023)
  3. 如果指数全部为0且尾数非零,则该数字被称为非规格化数

这基本上意味着总共有2^62-2^52+1个可能的double表示,根据标准,它们在0和1之间。请注意,2^52+1是为了消除非规格化数字的情况。

请记住,如果尾数为正但指数为负数,则数字为正但小于1 :-)

对于其他数字,它会更加困难,因为边缘整数数字可能无法以精确的方式表示在IEEE 754表示中,并且因为存在其他位用于指数以能够表示数字,因此数字越大,不同值就越低。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接