Сколько двойных чисел существует между 0,0 и 1,0?
Это то, о чем я думал в течение многих лет, но у меня никогда не было времени, чтобы спросить раньше.
Многие (псевдо) генераторы случайных чисел генерируют случайные числа в диапазоне от 0,0 до 1,0. Математически в этом диапазоне есть бесконечные числа, но double
является числом с плавающей запятой и, следовательно, имеет конечную точность.
Итак, вопросы:
- Только сколько
double
числа есть между 0,0 и 1,0? - Есть ли столько же чисел от 1 до 2? Между 100 и 101? Между 10^100 и 10^100+1?
Примечание: если это имеет значение, меня интересует определение Java double
особенно.
6 ответов
Джава double
s в формате IEEE-754, поэтому они имеют 52-битную дробь; между любыми двумя смежными степенями двух (включая одну и исключая следующую), следовательно, будет от 2 до 52-й степени, отличной double
с (т. е. 4503599627370496 из них). Например, это число различных double
s между 0,5 включенным и 1,0 исключенным, и точно так же, что многие также лежат между 1,0 включенным и 2,0 исключенным, и так далее.
Подсчет doubles
между 0,0 и 1,0 сложнее, чем делать это между степенями двух, потому что в этот диапазон входит много степеней двух, и, кроме того, каждый попадает в острые проблемы денормализованных чисел. 10 из 11 битов показателей степени охватывают рассматриваемый диапазон, включая денормализованные числа (и я думаю, что несколько видов NaN
) вы бы в 1024 раза double
как между двумя силами - не более 2**62
в общем все равно. Исключая денормализованный & C, я считаю, что счет будет 1023 раза 2**52
,
Для произвольного диапазона, например от 100 до 100,1, это еще сложнее, потому что верхняя граница не может быть точно представлена в виде double
(не быть точным кратным любой степени двух). В качестве удобного приближения, поскольку прогрессия между степенями двух линейна, можно сказать, что указанный диапазон 0.1 / 64
й промежуток между окружающими силами двух (64 и 128), так что вы ожидаете около
(0.1 / 64) * 2**52
отчетливый double
с - что приходит к 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 нет. Ни одно из этих чисел не представляется в двойной точности, и между ними нет двойников. Ближайшие два числа двойной точности:
99999999999999982163600188718701095...
а также
10000000000000000159028911097599180...
Другие уже объяснили, что в диапазоне около 2^62 двойников [0.0, 1.0].
(Неудивительно, что существует почти 2^64 различных конечных двойных чисел; из них половина положительна, а примерно половина из них < 1,0.)
Но вы упомянули генераторы случайных чисел: обратите внимание, что генератор случайных чисел, генерирующий числа от 0,0 до 1,0, в общем случае не может генерировать все эти числа; как правило, он будет производить только числа вида n/2^53 с n целым числом (см., например, документацию Java для nextDouble). Таким образом, обычно есть только около 2^53 (+/-1, в зависимости от того, какие конечные точки включены) возможные значения для random()
выход. Это означает, что большинство значений типа double в [0.0, 1.0] никогда не будут сгенерированы.
Статья Новая математика Java: Часть 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 для единицы наименьшей точности или единицы в последнем месте.
Двойной Java - это двоичное число IEEE 754.
Это означает, что нам нужно учитывать:
- Мантисса 52 бит
- Экспонента - это 11-битное число с 1023 смещением (то есть с добавлением 1023)
- Если показатель степени равен 0, а мантисса отлична от нуля, то число называется ненормализованным
Это в основном означает, что в общей сложности существует 2^62-2^52+1 возможных двойных представлений, которые в соответствии со стандартом находятся между 0 и 1. Обратите внимание, что 2 ^ 52 + 1 предназначен для устранения случаев ненормализованных номера.
Помните, что если мантисса положительна, а показатель степени отрицателен, число положительно, но меньше 1:-)
Для других чисел это немного сложнее, потому что целочисленные числа ребер могут не быть точно представлены в представлении IEEE 754, и потому что в показателе степени используются другие биты, чтобы иметь возможность представлять числа, поэтому чем больше число, тем меньше разные значения.