C ++: очистить биты с плавающей точкой одинарной точности - PullRequest
5 голосов
/ 26 августа 2011

В настоящее время я конвертирую программу, которая изначально предназначалась для OpenCL, в C ++, и у меня возникли некоторые проблемы с одной конкретной ее частью.

Одно из выражений, обычно используемых в указанной программе, включает в себя 32-разрядное значение с плавающей запятой, преобразование его в целое число (т.е. не округление его до целого числа, а интерпретацию тех же данных, что и int - think reinterpret_cast), выполнение некоторого бита закручивая магию на нем и затем преобразовывая его обратно в число с плавающей точкой (еще раз, не фактическое преобразование, а реинтерпретация тех же данных). Хотя это хорошо работает в OpenCL, с C ++ и gcc это нарушает строгие правила псевдонимов, нарушая программу, если оптимизация включена, и, в зависимости от архитектуры, может потребовать дорогостоящего хранения load-hit-store, поскольку регистры с плавающей запятой и целочисленные регистры разделены.

Мне удалось эффективно избежать большинства этих выражений, но есть одно, я не уверен, можно ли сделать это быстрее. По сути, цель состоит в том, чтобы очистить несколько бит справа от числа с плавающей запятой; код OpenCL делает это примерно так:

float ClearFloatBits(float Value, int NumberOfBits) {
    return __int_as_float((__float_as_int(Value) >> NumberOfBits) << NumberOfBits);
}

Поскольку это по существу округление от указанной (двоичной) цифры, моя версия C ++ теперь выглядит так:

float ClearFloatBits(float Value, int NumberOfBits) {
    float Factor = pow(2.0f, 23 - NumberOfBits);

    return ((int)(Value*Factor))/Factor;
}

Там, где pow и деление, конечно, заменены поиском LUT и соответствующим умножением, здесь опущено для лучшей читаемости.

Есть ли лучший способ сделать это? Что меня особенно беспокоит, так это (int) преобразование в округление вниз, которое, я думаю, является самой дорогой частью. Гарантируется, что число с плавающей точкой, передаваемое функции, представляет собой число от 1,0 (включительно) до 2,0 (исключая), если это помогает.

Заранее спасибо

Ответы [ 3 ]

3 голосов
/ 26 августа 2011

Вместо этого используйте взломать объединение:

float ClearFloatBits(float Value, int NumberOfBits) {
   union { unsigned int int_val; float flt_val; } union_hack;
   union_hack.flt_val = Value;
   (union_hack.int_val >>= NumberOfBits) <<= NumberOfBits;
   return union_hack.flt_val;
}

Строго говоря, это неопределенное поведение.В соответствии со стандартами C и C ++, запрещено записывать результат записи в один член объединения, а затем читать из другого члена без предварительной записи в этот другой член.* Однако такое использование союзов настолько широко и столь древне, что ни один из известных мне авторов компиляторов не подчиняется стандарту.На практике поведение очень четко определено и это именно то, что вы ожидаете.Тем не менее, этот хак может не сработать, если перенести его на какой-то очень странный компьютер с архитектурой, который использует очень строго соответствующий компилятор.

2 голосов
/ 26 августа 2011

Переинтерпретация как int нарушает правила наложения имен. Переосмысление как unsigned char[4] не делает. Вам нужно поддерживать NumberOfBits значения> = 8? Если нет, вы можете просто сделать битовый сдвиг на ptr[3]

0 голосов
/ 26 августа 2011

Разве вы не можете использовать floor () вместо преобразования в int?

...