У меня такая задача. Дано целое беззнаковое число 32-битное или 64-битное.
Надо поотдельности вытащить из него младшие 16 бит, следующие 16 бит
и 16 бит после них.
Вопрос, как лучше всего сделать? У меня два варианта:
1.
Здравствуйте, ra88, Вы писали:
R>Приветствую!
R>У меня такая задача. Дано целое беззнаковое число 32-битное или 64-битное. R>Надо поотдельности вытащить из него младшие 16 бит, следующие 16 бит R>и 16 бит после них.
R>Вопрос, как лучше всего сделать? У меня два варианта: R>1. R>
R>typedef unsigned long long uint64;
R>typedef unsigned int uint32;
R>typedef unsigned short uint16;
R>#define HH16(x) ((uint16)(((uint64)x & 0xFFFF00000000LU) >> 32))
R>#define HI16(x) ((uint16)(((uint64)x & 0xFFFF0000LU) >> 16))
R>#define LO16(x) ((uint16)((uint64)x & 0xFFFFLU))
R>// затем:
R>....
R>uint64 x = 328237324242L;
R>uint32 y = 324982372
R>printf("word0 = %u, word1= %u, word2 = %u\n", HH16(x), HI16(x), LO16(x));
R>printf("word0 = %u, word1= %u, word2 = %u\n", HH16(y), HI16(y), LO16(y));
быстрее
А ещё можно сделать так:
R>#define HH16(x) ((uint16)((((uint64)x << 16) >> 48))
R>#define HI16(x) ((uint16)((((uint64)x << 32) >> 48))
R>#define LO16(x) ((uint16)((((uint64)x << 48) >> 48))
скорее всего будет ещё быстрее :)
R>
R>либо R>2.
Будут проблеммы на big endian, и скорее всего будет медленней, но не факт — зависит от компилятора.
Здравствуйте, ra88, Вы писали:
R>Кто из них быстрее? Не будет ли проблем у второго метода на big endian машине c другим компилятором?
У второго метода много проблем.
Во-первых — действительно будут проблемы на big endian.
Во-вторых, грубая "реинтерпретация" памяти занатого объектом одного типа, как объект другого типа приводит в С/С++ к неопределенному поведению (с редкими исключениями). Это выливается во вполне реальные проблемы в том же компиляторе gcc, который уже в режиме оптимизации -O2 подразумевает 'strict aliasing'
Сделать "второй вариант" можно, но не так, как в исходном примере. Чтобы это работало в gcc, реинтерпретация должна либо делаться через тип 'char', либо через union, содержащий поле исходного типа и набор полей целевого типа.
Здравствуйте, ra88, Вы писали:
R>Приветствую!
R>У меня такая задача. Дано целое беззнаковое число 32-битное или 64-битное. R>Надо поотдельности вытащить из него младшие 16 бит, следующие 16 бит R>и 16 бит после них.
Не понял, что значит "следующие 16 бит" и "16 бит после них"?
А вообще конечно union с массивами простых типов.. и индексы в зависимости от little/big endian.. imho.
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Сделать "второй вариант" можно, но не так, как в исходном примере. Чтобы это работало в gcc, реинтерпретация должна либо делаться через тип 'char', либо через union, содержащий поле исходного типа и набор полей целевого типа.
Насколько я понимаю, использование union для этих целей также приводит к UB. Ведь мы читаем/пишем при помощи не указателя на union, а указателя на его member, и доступ происходит по типу мембера => отсюда все те же проблемы
Здравствуйте, valker, Вы писали:
V>А вообще конечно union с массивами простых типов.. и индексы в зависимости от little/big endian.. imho.
Ну и зачем union каст, который:
1. имеет зависимые от endian индексы.
2. несмотря на поддержку компиляторами, таки не соответствует стандарту.
3. вряд ли приведёт к генерации лучшего кода, чем получается через сдвиги.
Единственно, нужно посмотреть — не косячит ли компилятор на больших сдвигах маленьких чисел. Т.е.
uint16 x;
uint16 w0 = Word0(x); // тут всё в порядке
uint16 w1 = Word1(x), // x>>16 =!!!= x>>(16%16) == x>>0 == x, а должно было быть 0
uint32 y;
uint16 w2 = Word2(y); // y>>16>>16 == y>>32 =!!!= y>>(32%32) == y>>0 == y
В этом случае нужно пропатчить — расширив тип аргумента до ожидаемого и нормально работающего
А ещё — интересно, какое поведение ожидается от знаковых чисел? Т.е. -1 трактуется как "абсолютный", 0xFFFF......F, или как только 32-битный 0x00000000FFFFFFFF (ну или какая там разрядность у int)?
И под это дело подкручивать формулы.
Либо можно договориться, что аргументы этих макросов — строго беззнаковые, а кто подсунул левятину — ССЗБ.
R>typedef unsigned long long uint64;
R>typedef unsigned int uint32;
R>typedef unsigned short uint16;
R>#define HH16(x) ((uint16)(((uint64)x & 0xFFFF00000000LU) >> 32))
R>#define HI16(x) ((uint16)(((uint64)x & 0xFFFF0000LU) >> 16))
R>#define LO16(x) ((uint16)((uint64)x & 0xFFFFLU))
И если идти по этому пути, то, если тип 'uint16' именно содержит ровно 16 требуемых битов, то вышеприведенные операции можно записать проще. В частности, 'LO16' можно записать как просто
[ccode]
#define LO16(x) ((uint16)(x))
и не надо никаких явных битовых операций. А компилятор уже сам разберется, как ему лучше выполнить это приведение.