Точно преобразовать float 32 в unsigned short или unsigned char

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

Я пишу небольшую программу, которая будет использоваться для преобразования 32-битных значений с плавающей запятой в короткие значения int (16 бит) и unsigned char (8 бит). Это предназначено для HDR-изображений.

Из здесь я мог получить следующая функция (без зажима):

static inline uint8_t u8fromfloat(float x)
{
    return (int)(x * 255.0f);
}

Я полагаю, что таким же образом мы могли бы получить короткий int, умножив на (pow( 2,16 ) -1)

Но потом я подумал об упорядоченном дизеринге и особенно о дизеринге по Байеру. Чтобы преобразовать в uint8_t, я полагаю, я мог бы использовать матрицу 4x4 и матрицу 8x8 для беззнакового короткого замыкания.

Я также подумал о справочной таблице для ускорения процесса следующим образом:

uint16_t LUT[0x10000] // 2¹⁶ values contained

и сохраните 2 ^ 16 коротких значений без знака, соответствующих поплавку. Эту же таблицу можно было бы затем использовать и для uint8_t из-за неявного приведения между unsigned short ↔ unsigned int

Но разве подобная справочная таблица не займет много памяти? Кроме того, как можно заполнить такую ​​таблицу?!

Теперь я в замешательстве, что было бы лучше по вашему мнению?

РЕДАКТИРОВАТЬ после ответа uwind: скажем теперь, что я также хочу одновременно выполнить базовое преобразование цветового пространства, то есть перед преобразованием в U8/U16, выполнить преобразование цветового пространства (с плавающей запятой), а затем уменьшить его до U8/U16. . Не будет ли в этом случае использование LUT более эффективным? И да, у меня все еще была бы проблема с индексацией LUT.


person Lex    schedule 08.01.2013    source источник
comment
Я предположил, что это вопрос C.   -  person Jodrell    schedule 08.01.2013
comment
да, забыл уточнить, это C/C++   -  person Lex    schedule 08.01.2013


Ответы (3)


На мой взгляд, справочная таблица не поможет, поскольку для индексации в нее нужно преобразовать число с плавающей запятой в некоторый целочисленный тип. Словить 22.

Для таблицы потребуется 0x10000 * sizeof (uint16_t) байт, что составляет 128 КБ. Не так много по современным меркам, но с другой стороны кеш ценен. Но, как я уже сказал, таблица мало что добавляет к решению, поскольку вам нужно преобразовать число с плавающей запятой в целое число для индексации.

Вы можете сделать таблицу, проиндексированную необработанными битами числа с плавающей запятой, переинтерпретированным как целое число, но это должно быть 32 бита, что становится очень большим (8 ГБ или около того).

Перейдите к прямому преобразованию во время выполнения, которое вы описали.

person unwind    schedule 08.01.2013
comment
Спасибо расслабиться за ваш вклад. Предположим теперь, что я также хочу одновременно выполнить базовое преобразование цветового пространства, то есть перед преобразованием в U8/U16 выполнить преобразование цветового пространства (в плавающем), а затем уменьшить его до U8/U16. Не будет ли в этом случае более эффективным использование лут? И да, у меня все еще была бы проблема с индексацией лут... - person Lex; 08.01.2013

Просто оставайтесь с умножением - все будет хорошо.

Практически все современные процессоры имеют векторные инструкции (SSE, AVX, ...), адаптированные для этого, поэтому вы можете обратиться к программированию для этого. Или используйте компилятор, который автоматически векторизует ваш код, если это возможно (Intel C, также GCC). Даже в тех случаях, когда поиск по таблице является возможным решением, это часто может быть быстрее, потому что вы не страдаете от задержки памяти.

person Chris    schedule 08.01.2013

Во-первых, следует отметить, что float имеет 24-битную точность, которая никак не может вписаться в 16-битный int или даже 8-битный. Во-вторых, у float гораздо больший диапазон, который нельзя сохранить ни в int, ни в long long int.

Таким образом, заголовок вашего вопроса на самом деле неверный, и нет возможности точно преобразовать любое число с плавающей запятой в короткое или символьное. Вы хотите сопоставить значение с плавающей точкой от 0 до 1 с 8-битным или 16-битным диапазоном целых чисел.


Для кода, который вы используете выше, он будет работать нормально. Однако крайне маловероятно, что значение 255 будет возвращено, поскольку для него требуется ровно 1,0 в качестве входных данных, иначе такие значения, как 254,99999, будут усечены до 254. Значение следует округлить. вместо

return (int)(x * 255.0f + .5f);

или лучше, используйте код, указанный в вашей ссылке, для более сбалансированного распределения.

static inline uint8_t u8fromfloat_trick(float x)
{
    union { float f; uint32_t i; } u;
    u.f = 32768.0f + x * (255.0f / 256.0f);
    return (uint8_t)u.i;
}

Использование LUT не будет быстрее, потому что таблица для 16-битных значений слишком велика для размещения в кеше и фактически может значительно снизить производительность. Фрагменту выше нужны только 2 инструкции с плавающей запятой или только 1 с FMA. И SIMD повысит производительность в 4-32 раза (или больше), поэтому метод LUT будет легко превзойден, поскольку гораздо сложнее распараллелить поиск в таблице.

person phuclv    schedule 09.10.2013