Округление с плавающей точкой при усечении - PullRequest
3 голосов
/ 14 марта 2009

Вероятно, это вопрос к эксперту x86 FPU:

Я пытаюсь написать функцию, которая генерирует случайное значение с плавающей запятой в диапазоне [min, max]. Проблема в том, что мой алгоритм генератора (Mersenne Twister с плавающей точкой, если вам любопытно) возвращает только значения в диапазоне [1,2) - то есть я хочу включить верхнюю границу, но мое сгенерированное «исходное» значение из эксклюзивной верхней границы. Подвох в том, что базовый генератор возвращает 8-байтовое двойное число, но мне нужно только 4-байтовое число с плавающей запятой, и я использую режим округления FPU по умолчанию - Nearest.

Что я хочу знать, так это то, приведет ли само усечение в этом случае к тому, что мое возвращаемое значение будет включать в себя значение max, когда внутреннее 80-битное значение FPU достаточно близко, или я должен увеличить значение моего максимального значения до того, как умножение его на промежуточное случайное число в [1,2), или я должен изменить режимы FPU. Или любые другие идеи, конечно.

Вот код, который я сейчас использую, и я проверил, что 1.0f преобразуется в 0x3f800000:

float MersenneFloat( float min, float max )
{
    //genrand returns a double in [1,2)
    const float random = (float)genrand_close1_open2(); 
    //return in desired range
    return min + ( random - 1.0f ) * (max - min);
}

Если это имеет значение, это должно работать как на Win32 MSVC ++, так и на Linux gcc. Кроме того, использование каких-либо версий оптимизации SSE изменит ответ на этот вопрос?

Редактировать: Ответ - да, усечение в этом случае от двойного до плавающего достаточно, чтобы результат включал макс. См. Ответ Crashworks для более подробной информации.

Ответы [ 3 ]

4 голосов
/ 14 марта 2009

Операции SSE будут тонко изменять поведение этого алгоритма, потому что они не имеют промежуточного 80-битного представления - математика действительно выполняется в 32 или 64 битах. Хорошей новостью является то, что вы можете легко протестировать его и посмотреть, изменит ли он ваши результаты, просто указав параметр / ARCH: SSE2 в командной строке для MSVC, что заставит его использовать скалярные операции SSE вместо команд FPU x87 для обычной плавающей запятой математика.

Я не уверен, насколько точно точное поведение округления происходит вокруг целочисленных границ, но вы можете проверить, что произойдет, когда 1.999 .. округляется с 64 до 32 битов на например

static uint64 OnePointNineRepeating = 0x3FF FFFFF FFFF FFFF // exponent 0 (biased to 1023), all 1 bits in mantissa
double asDouble = *(double *)(&OnePointNineRepeating);
float asFloat = asDouble;
return asFloat;

Редактировать, результат: оригинальный постер выполнил этот тест и обнаружил, что с усечением 1.99999 округляется до 2 как с /arch:SSE2.

, так и без него
0 голосов
/ 14 марта 2009

С усечением вы никогда не включите макс.

Вы уверены, что вам действительно нужен максимум? Буквально почти 0 шансов, что вы попадете точно на максимум.

Тем не менее, вы можете использовать тот факт, что вы отказываетесь от точности, и делать что-то вроде этого:

float MersenneFloat( float min, float max )
{
    double random = 100000.0; // just a dummy value
    while ((float)random > 65535.0)
    {
        //genrand returns a double in [1,2)
        double random = genrand_close1_open2() - 1.0; // now it's [0,1)
        random *= 65536.0; // now it's [0,65536). We try again if it's > 65535.0
    }
    //return in desired range
    return min + float(random/65535.0) * (max - min);
}

Обратите внимание, что теперь у вас есть небольшая вероятность нескольких вызовов genrand при каждом вызове MersenneFloat. Таким образом, вы отказались от возможной производительности за короткий промежуток времени. Поскольку вы переходите с удвоения на поплавок, вы не жертвуете точностью.

Редактировать: улучшенный алгоритм

0 голосов
/ 14 марта 2009

Если вы отрегулируете округление таким образом, чтобы оно охватывало оба конца диапазона, не будут ли эти экстремальные значения вдвое менее вероятными, чем любые неэкстремальные?

...