Самый большой диапазон равномерно распределенных значений с плавающей запятой?

Я пытаюсь лучше понять распределение значений с плавающей запятой в строке действительных чисел.

Я написал этот код для подсчета количества равномерно распределенных представляемых значений в диапазоне (-R,R), где R — степень 10 (также пытался использовать степени 2). ):

public class Foo {
    public static void main(String[] args)
    {
        for(int i=0; i<24; i++)
        {
            int count = 0;
            float R = (float) Math.pow(10, i); //(2<<i);
            float Rstep = Math.ulp(R);
            for(float x = -R; x <= R; x+=Rstep)
                count++;
            System.out.println(R+" "+count+" "+Math.ulp(R));
        }
    }
}

Я был удивлен разницей в результатах, т.е.

1.0 16777217 1.1920929E-7
10.0 20971521 9.536743E-7
100.0 26214401 7.6293945E-6
1000.0 32768001 6.1035156E-5
10000.0 20480001 9.765625E-4
100000.0 25600001 0.0078125
1000000.0 32000001 0.0625

поскольку я наполовину убедил себя, что количество равномерно распределенных значений будет 16777216 (т.е. 1‹‹23 для 23-битной мантиссы, удвоенной из-за знакового бита).

Чтобы конкретизировать этот вопрос, я пытаюсь построить модель (в которой используются единицы СИ с точностью на несколько порядков, например, расстояние в км до нанометров), но мне нужно отобразить ее в плавающем пространстве (для загрузки в графический процессор). Поскольку это научная модель, мне нужно понять, где теряется точность. План состоит в том, чтобы привязать значения к равномерно распределенному диапазону, поэтому из приведенной выше таблицы привязка к диапазону (-1000,1000) даст мне 32768001 точное значение.

Мне кажется нелогичным, что в этих диапазонах будет так много различий, и почему мощность двух случаев ограничена.

Кто-нибудь может объяснить, как думать об этом?

ваше здоровье


person user2152466    schedule 22.03.2013    source источник
comment
Я думаю, вы забыли о нуле.   -  person alestanis    schedule 23.03.2013
comment
спасибо - я не так беспокоюсь об этой потенциальной ошибке на единицу (хотя было бы хорошо понять) - больше дисперсия в 2 раза   -  person user2152466    schedule 23.03.2013
comment
Что вы подразумеваете под «равномерно распределенным»? Единственные интервалы, включающие ноль, где значения float имеют одинаковый интервал, составляют от -2 ** -125 (около -2,35e-38) до +2 ** -125 и все его подинтервалы. В этом интервале интервал составляет 2 **-149 (около 1,40e-45). Кроме того, размер шага варьируется; распределение представляемых значений неравномерно.   -  person Eric Postpischil    schedule 23.03.2013
comment
@EricPostpischil: [-1.0, +1.0] — уникальный максимизатор для любого двоичного формата FP, поскольку это единственная степень двойки, которая также является степенью десятка. Спрашивающий ошибся в подсчете, взяв за единицу ULP больше 1,0, что в два раза больше.   -  person Stephen Canon    schedule 23.03.2013


Ответы (1)


Вы действительно должны печатать поплавки в шестнадцатеричном формате, тогда это будет намного понятнее.

Ваша оценка «16777216 (т. е. 1‹‹23 для 23-битной мантиссы, удвоенная из-за знакового бита)» — это только половина того, что вы можете ожидать. Лучше всего начать с числа, которое выглядит как -0x1.FFF…pX в шестнадцатеричном формате, то есть противоположно числу чуть ниже степени двойки. При повторном добавлении начального ULP вы действительно пройдете все значения мантиссы с показателем степени X. Это 1‹‹23 шага, как вы сделали вывод. Когда вы закончите с этим, вы будете вдвое ближе к нулю, чем в начале. То же количество шагов приведет вас к нулю (с показателями меньше X), а затем снова удвойте количество шагов для положительных значений.

Итак, это 1‹‹25 (~32000000) или около того равномерно расположенных чисел с плавающей запятой, которые можно найти между любой степенью двойки и ее противоположностью. Вы получаете примерно такое же количество шагов с 1000, потому что 1000 меньше, чем две 1024.

В худшем случае, как вы заметили, нужно начинать с числа чуть выше степени двойки, скажем, 0x1.00001pX. Затем вы почти не просматриваете значения с показателем X, а вместо этого сразу же начинаете посещать значения с более низкими показателями. В итоге вы посещаете вдвое меньше значений, чем если бы вы начали с 0x1.FFF…pX.


ПРИМЕЧАНИЕ: нотация -0x1.123defpX должна интерпретироваться как -0x1.123def * 2 ^ X. Возможно, ваш язык программирования принимает ее для ввода и/или вывода значений с плавающей запятой. Повторюсь, очень удобно, когда пытаешься понять, что происходит.

person Pascal Cuoq    schedule 22.03.2013