这是我多年来一直在思考的问题,但以前从未花时间询问过。
许多(伪)随机数生成器生成0.0到1.0之间的随机数。从数学上讲,在此范围内有无限多的数字,但double
是浮点数,因此具有有限的精度。
所以问题是:
- 在0.0和1.0之间有多少个
double
数字? - 在1和2之间有同样多的数字吗?在100和101之间?在10^100和10^100+1之间?
注意:如果有差异,我特别关注Java对double
的定义。
这是我多年来一直在思考的问题,但以前从未花时间询问过。
许多(伪)随机数生成器生成0.0到1.0之间的随机数。从数学上讲,在此范围内有无限多的数字,但double
是浮点数,因此具有有限的精度。
所以问题是:
double
数字?注意:如果有差异,我特别关注Java对double
的定义。
对于类似"100到100.1"这样的任意范围,情况更加复杂,因为上限不能被表示为一个准确的double
(不是2的任何幂的准确倍数)。作为一个便捷的近似,由于两个幂次之间的进展是线性的,你可以说该范围是包围两个幂次之间跨度的0.1 / 64
,所以你应该期望大约
(0.1 / 64) * 2**52
distinct double
s -- 这大约是 7036874417766.4004
,误差在一两个左右 ;-).
所有double
类型值的表示范围在0x0000000000000000
和0x3ff0000000000000
之间,位于区间[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之间没有任何double类型的值。这两个数字都无法在双精度浮点数中表示,并且没有双精度浮点数落在它们之间。最接近的两个双精度浮点数是:
99999999999999982163600188718701095...
< p>and < /p>
和10000000000000000159028911097599180...
但是你提到了随机数生成器:请注意,生成介于 0.0 和 1.0 之间的随机数的随机数生成器通常 不能 产生所有这些数字;通常它只能产生形如 n/2^53 的数字,其中 n 是整数(例如,请参见 Java 文档中的 nextDouble)。因此,random()
输出的可能值通常只有大约 2^53 (+/-1,具体取决于包含哪些端点)。这意味着在 [0.0,1.0] 中的大多数双精度浮点数将永远不会被生成。
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 Martellidouble
在相邻的2的幂之间进行计数大约需要52天(当然,无论如何,println
都不太可能以任何速度运行,所以让我们假设这个语句消失了;-)。我认为,在一台强大但现实的机器上,需要一年或更短的时间就可以完成。;-) - Alex Martelli更多信息请参见维基百科文章。
1
是错误的,因为隐藏位始终为一--因此,有2^52
而不是2^53
个不同的值(在相邻的两个二次幂之间,一个包括在内,下一个排除在外--而不是在0.0和1.0之间!)。 - Alex MartelliJava中的double是IEEE 754二进制64位数字。
这意味着我们需要考虑:
这基本上意味着总共有2^62-2^52+1个可能的double表示,根据标准,它们在0和1之间。请注意,2^52+1是为了消除非规格化数字的情况。
请记住,如果尾数为正但指数为负数,则数字为正但小于1 :-)
对于其他数字,它会更加困难,因为边缘整数数字可能无法以精确的方式表示在IEEE 754表示中,并且因为存在其他位用于指数以能够表示数字,因此数字越大,不同值就越低。