Нестандартные форматы float'ов в OpenGL

OpenGL для хранения данных в текстурах использует ряд нестандартных floating-point форматов, наиболее известным из которых является 16-битовый (так называемый half). Однако кроме него есть и другие подобные форматы, например 11 и 10-битовые беззнаковые.

Для начала рассмотрим как устроен классический 32-битовый тип float. Его можно представить как набор целочисленных беззнаковых полей - знак s (1 бит), экспонента e(8 бит) и мантисса m (23 бита)(см. рис 1.)

Рис 1. Строение стандартного 32-битового float'а.

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

uint32_t fr = *(uint32_t *) &f;             // get bit's from floating point value f
uint32_t s  = fr >> 32;
uint32_t e  = (fr >> 23) & 0xFF;
uint32_t m  = fr & 7FFFFF;

В простейшем случае, когда экспонента e не принимает минимального и максимального значений (0 и 255), то соответствующее floating-point значение f может быть получено по следующей формуле:

Здесь через M обозначено число бит мантиссы, а через B так называемый bias, равный для данного типа 128. Это так называемые нормализованные числа, двоичная запись которых имеет следующий вид - 2k1.nnnnnnn, через nnnnn обозначены биты мантиссы (фактически старший бит мантиссы, равный единице просто в записи опускается).

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

Прежде всего это так называемые денормализованные числа, они соответствуют случаю, когда экспонента равна нулю, а мантисса нет (e = 0, m > 0).

Если и экспонента и мантисса равны нулю (e = m = 0), то мы имеем дело с нулем. При этом ноль может иметь знак.

Случай, когда e=255 и m=0 соответствует плюс или минус бесконечности (Inf). Последний оставшийся случай (e=255, m > 0) соответствует так называемым NaN (от Not a Number).

Half-float формат

Этот формат (соответствующий тип данных в OpenGL - GL_HALF_FLOAT) построен по подобию 32-битовых чисел, но при этом длина мантиссы M составляет 10 бит, а длина экспоненты - 5 бит (bias B для этого типа равен 15).

Рис 2. Строение 16-битового floating-а.

Выделение всех основных полей также просто как и для 32-битового случая.

uint32_t s  = hr >> 15;
uint32_t e  = (fr >> 10) & 0x1F;
uint32_t m  = fr & 0x3FF;

Соответственно правила преобразования в float выглядят следующим образом. Для нормализованных чисел (0 < e < 31) значение задается следующей формулой:

Для денормализованных чисел (e = 0, m > 0):

Нулю соответствует случай e = 0, m = 0. Бесконечности соответствует случай e = 31, m = 0. И NaN'ам соответствует случай e = 31, m > 0.

Рассмотрим теперь как можно преобразовывать 32-битовые float'ы в 16-битовые и наоборот. Для случая когда используются только нормализованные числа, можно обойтись исключительно побитовыми операциями.

f = ((h & 0x8000)<<16) | (((h  & 0x7c00)+0x1C000)<<13) | ((h & 0x03FF)<<13);
h = ((f>>16) & 0x8000) | ((((f & 0x7f800000)-0x38000000)>>13) & 0x7c00) | ((f>>13) & 0x03ff);

Однако разбор общего случая потребует целого ряда проверок, что весьма нежелательно, поскольку обычно приходится преобразовывать сразу большой набор чисел. Поэтому гораздо более удобным оказывается поход, основанный на использовании нескольких look-up таблиц. Ниже приводится осуществление перевода меду форматами с использованием таких таблиц.

h = baseTable [(f>>23) & 0x1ff] + ((f & 0x007fffff)>>shiftTable [(f >> 23) & 0x1ff]);
f = mantTable [offsTable [h >> 10] + (h & 0x3ff)] + expTable [h >> 10];

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

uint32_t    mantTable  [2048];
uint32_t    expTable   [64];
uint16_t    offsTable  [64];
uint16_t    baseTable  [512];
uint8_t     shiftTable [512];

11-битовые и 10-битовые беззнаковые числа float

Текстурный формат GL_R11F_G11F_B10F вводит сразу два новых формата float-чисел - беззнаковые 10 и 11-битовые числа (при этом в три таких числа упакованы в один 32-битовый тексел).

В каждом таком числе отсутствует знаковый бит, экспонента занимает 8 бит (как и для 16-битовых float) с B=15, а мантисса занимает 6 или 5 бит (см. рис. 3).

Рис 3. Строение 11-битового и 10-битового беззнаковых float-ов.

Для 11-битовых чисел действуют следующие правила перевода. Нормализованные числа (0 < e < 31) переводятся по формуле

Денормализованные (e = 0, m > 0) переводятся по формуле

Нулю соответствует случай e = 0, m = 0. Бесконечности соответствует случай e = 31, m = 0. И NaN'ам соответствует случай e = 31, m > 0.

Для 10-битовых все разница заключается в другом числе бит для мантиссы и в формулах переводя меняется степень двойки.

Нормализованные числа переводятся по формуле

Денормализованные переводятся по формуле

Очень много полезной информации о строении чисел с плавающей точкой можно найти в следующей статье: What Every Computer Scientist Should Know About Floating-Point Arithmetic.

По этой ссылке можно скачать весь исходный код к этой статье. Также доступны для скачивания откомпилированные версии для M$ Windows и Linux.

Используются технологии uCoz