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


93

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

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

所以问题是:

  1. double0.0和1.0之间有多少个数字?
  2. 1和2之间的数字是否一样多?在100和101之间?在10 ^ 100和10 ^ 100 + 1之间?

注意:如果有所作为,我对Java的定义double特别感兴趣。

Answers:


68

Java double采用IEEE-754格式,因此它们有52位的分数;因此,在任意两个相邻的2的幂之间(包括一个double且不包括下一个),将有2到52的幂不同(即其中的4503599627370496)。例如,这是double包含0.5到排除1.0之间的distance s 的数量,也恰好有很多位于包含1.0到排除2.0之间的distance s,依此类推。

doubles在0.0和1.0之间进行计数比在2的幂之间进行计数更困难,因为该范围内包含2的很多幂,而且,一个也进入了非正规数的棘手问题。指数的11位中有10位涵盖了所讨论的范围,因此,包括非规格化的数字(我认为是几种NaN),您的doubles的幂是1024 的2的幂次之和- 2**62总数之和。不包括非规范化的&c,我相信这个数字将是1023次2**52

对于“ 100到100.1”之类的任意范围,这甚至更加困难,因为上限不能精确地表示为a double(不是2的幂的精确倍数)。作为一个方便的近似值,由于2的幂之间的线性关系是线性的,因此您可以说该范围是0.1 / 642的幂(64和128)之间的跨度的th,因此您可以期望

(0.1 / 64) * 2**52

与众不同的doubles-得出7036874417766.4004...或给出一或两个;-)。


@Alex:请注意,当我写100到100.1时,我写错了。我的意思是100至101基本上,N和N + 1之间,用于任意N.
polygenelubricants

4
@Alex:让我明白一点:不可能有更多的2**64double值(因为它是64位类型),而且显然这些值中有很大一部分位于它们之间0..1
polygenelubricants 2010年

9
@polygene,是的,是的-具体来说,大约四分之一的可能值(对于任何基数和指数与分数长度的任何“正常”浮点表示形式)都介于0.0和1.0之间(另一四分之一介于1.0和无穷大之间,并且剩余部分在实轴的负一半上)。本质上,指数值的一半(正偏,在其范围的一半)表示基数的负幂,因此数字<1.0。
Alex Martelli 2010年

8
@polygenelubricants:在许多应用中,0到1的范围比100到101的范围重要得多,而且有趣,这就是为什么它在值中占有更大的份额的原因。例如,在物理学中,您通常必须处理荒谬的小值,例如牛顿的重力常数在6.67e-11。拥有良好的精度比在100和101之间有用。要了解更多信息,请阅读float-point-gui.de
Michael Borgwardt 2010年

1
您还可以将任何数字缩放到0.0到1.0之间,并分别跟踪缩放比例,从而减少计算误差。可以将整个数字行映射到两个数字之间,真是太好了!
codekaizen

42

每个double表示形式介于之间0x00000000000000000x3ff0000000000000位于[0.0,1.0]之间的值。那是(2 ^ 62-2 ^ 52)个不同的值(加上或减去一对,取决于您是否计算端点)。

间隔[1.0,2.0]对应于0x3ff0000000000000和之间的表示形式0x400000000000000。那是2 ^ 52个不同的值。

间隔[100.0,101.0]对应于0x4059000000000000和之间的表示形式0x4059400000000000;那是2 ^ 46个不同的值。

在10 ^ 100和10 ^ 100 +1之间没有双打。这些数字中的任何一个都不能用双精度来表示,并且它们之间没有双精度。最接近的两个双精度数字是:

99999999999999982163600188718701095...

10000000000000000159028911097599180...

+1,以获得准确支持的确切答案。(如果您对计数端点有些挑剔,请记住+0.0和-0.0具有不同的表示形式。)
Jim Lewis 2010年

1
+1,这样的转折结局!感觉就像我在阅读《 M. Night Shyamalan》剧本一样!
polygenelubricants 2010年

7

其他人已经解释说,在[0.0,1.0]范围内大约有2 ^ 62的两倍。
(不是真的令人吃惊:有近2 ^ 64个不同有限双打;其中一半是正的,和大约一半的那些是<1.0。)

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


3

Java的新数学文章,第2部分: IBM的浮点数提供了以下代码片段来解决此问题(以float形式,但我怀疑它也可以用于double形式):

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**23 -> 8388608两个相邻的大国之间的不同的值(当然,‘包容性’的部分意思是你要算一个,两个下一功率)。 doubles有52位分数!
Alex Martelli 2010年

1
@Alex:我想我必须离开程序(修改为双打),直到宇宙结束,才可以得到结果... :(
Mark Rushakoff 2010年

1
我觉得很蠢;我只是写了一篇double等价的文章,并以为“嘿,我会在大约5分钟内回答我自己的问题……”
polygenelubricants 2010年

1
@polygene:感觉就像一个Project Euler问题,其中明显的方法难以计算,但是必须有一些非常简单的公式来解决任意情况……
Mark Rushakoff 2010年

2
也许不是一个真正增压超级计算机:在一台机器只需花纳秒跑内环,以计数double的两个相邻的大国之间将需要约52天(println当然是非常不可能跑那么快,不管是什么,所以让我们假设一个陈述消失了;-)。我认为在功能强大但实际的机器上花费一年或更短的时间是可行的;-)。
Alex Martelli 2010年

2
  1. 2 ^ 53-64位浮点数(包括隐藏位)的有效位/尾数的大小。
  2. 大致上可以,因为sifnificand是固定的,但指数发生了变化。

有关更多信息,请参见Wikipedia文章


您对2的回答与我如何理解FP的工作相矛盾。
polygenelubricants 2010年

我认为1是错误的,因为隐藏位始终是一个-因此2^52没有 2^53 明显的值(两个相邻的大国,一个包括和下一个排除之间- !0.0和1.0之间)。
Alex Martelli 2010年

1

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表示形式精确地表示,并且因为指数中还使用其他位来表示数字,所以数字越大表示数字越小不同的值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.