Как обработать структуру с двумя неподписанными шортами, как если бы это было беззнаковое целое? (в С) - PullRequest
2 голосов
/ 26 октября 2010

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

typedef struct Fixed_t {
    unsigned short floor; //left side of the decimal point
    unsigned short fraction; //right side of the decimal point
} Fixed;

Теперь я хочу добавить два числа с фиксированной точкой, Fixed x и Fixed y.Для этого я рассматриваю их как целые числа и добавляю.

(Fixed) ( (int)x + (int)y );

Но, как говорит мой компилятор Visual Studio 2010, я не могу конвертировать между Fixed и int.

способ сделать это?

РЕДАКТИРОВАТЬ: Я не привержен {short floor, short fraction} реализации Fixed.

Ответы [ 9 ]

6 голосов
/ 26 октября 2010

Вы можете попытаться сделать неприятный хак, но здесь есть проблема с порядком байтов.Что бы вы ни делали для конвертации, откуда компилятор должен знать, что вы хотите, чтобы floor была самой значительной частью результата, а fraction - менее значимой частью?Любое решение, основанное на переинтерпретации памяти, будет работать для одного порядка байтов, но не для другого.

Вы должны либо:

(1) определить преобразование явно.Предполагая, что short равно 16 битам:

unsigned int val = (x.floor << 16) + x.fraction;

(2) измените Fixed так, чтобы вместо двух шортов он имел элемент int, а затем при необходимости разложил вместо составление при необходимости.

Если вы хотите, чтобы сложение было быстрым, то (2) это то, что нужно сделать.Если у вас 64-битный тип, то вы также можете выполнять умножение без декомпозиции: unsigned int result = (((uint64_t)x) * y) >> 16.

Между прочим, неприятный хак будет:

unsigned int val;
assert(sizeof(Fixed) == sizeof(unsigned int))              // could be a static test
assert(2 * sizeof(unsigned short) == sizeof(unsigned int)) // could be a static test
memcpy(&val, &x, sizeof(unsigned int));

Это будетработать в системе с прямым порядком байтов, где Fixed не имеет заполнения (а целочисленные типы не имеют битов заполнения).В системе с прямым порядком байтов вам нужно, чтобы члены Fixed находились в другом порядке, поэтому это неприятно.Иногда приведение к использованию через memcpy - это правильная вещь (в этом случае это скорее «трюк», чем «противный хак»).Это просто не тот случай.

5 голосов
/ 26 октября 2010

Если вам нужно, вы можете использовать союз, но остерегайтесь проблем с порядком байтов.Вы можете обнаружить, что арифметика не работает и, конечно, не является переносимой.

typedef struct Fixed_t {
   union { 
        struct { unsigned short floor; unsigned short fraction }; 
        unsigned int whole;
         };
} Fixed;

, которая, скорее всего, (я думаю) будет работать с прямым порядком байтов (а Windows / Intel - нет).

3 голосов
/ 26 октября 2010

Немного магии:

typedef union Fixed {
    uint16_t w[2];
    uint32_t d;
} Fixed;
#define Floor w[((Fixed){1}).d==1]
#define Fraction w[((Fixed){1}).d!=1]

Ключевые моменты:

  • Я использую целочисленные типы фиксированного размера, поэтому вы не зависите от short, являющегося 16-битным и int 32-битный.
  • Макросы для Floor и Fraction (с заглавными буквами, чтобы избежать конфликта с функцией floor()) обращаются к двум частям независимо от порядка байтов, например foo.Floor иfoo.Fraction.

Редактировать: По запросу OP пояснение к макросам:

Объединения - это способ объявления объекта, состоящего из нескольких различных перекрытийтипы.Здесь мы имеем uint16_t w[2]; перекрывающихся uint32_t d;, что позволяет получить доступ к значению в виде 2 16-битных единиц или 1 32-битной единицы.

(Fixed){1} - это составной литерал и может быть написано более подробно как (Fixed){{1,0}}.Его первый элемент (uint16_t w[2];) инициализируется с {1,0}.Затем выражение ((Fixed){1}).d оценивается как 32-разрядное целое число, первая 16-разрядная половина которого равна 1, а вторая 16-разрядная половина равна 0. В системе с прямым порядком байтов это значение равно 1, поэтому ((Fixed){1}).d==1 оценивается как 1(true) и ((Fixed){1}).d!=1 оценивается как 0 (false).В системе с прямым порядком байтов все будет наоборот.

Таким образом, в системе с прямым порядком байтов Floor равно w[1], а Fraction равно w[0].В системе с прямым порядком байтов Floor равно w[0], а Fraction равно w[1].В любом случае вы в конечном итоге сохраните / получите доступ к правильной половине 32-битного значения для порядкового номера вашей платформы.

Теоретически, гипотетическая система может использовать совершенно другое представление для 16-битных и32-битные значения (например, чередование битов двух половинок), ломая эти макросы.На практике этого не произойдет.: -)

2 голосов
/ 26 октября 2010

Это не возможно переносимо, так как компилятор не гарантирует, что Fixed будет использовать столько же места, сколько int. Правильный путь - определить функцию Fixed add(Fixed a, Fixed b).

1 голос
/ 26 октября 2010

Просто добавьте кусочки отдельно. Вам нужно знать значение дроби, которое означает «1» - здесь я называю это FRAC_MAX:

 // c = a + b
 void fixed_add( Fixed* a, Fixed* b, Fixed* c){
     unsigned short carry = 0;
     if((int)(a->floor) + (int)(b->floor) > FRAC_MAX){
         carry = 1;
         c->fraction = a->floor + b->floor - FRAC_MAX; 
     }
     c->floor = a->floor + b->floor + carry;
 }

В качестве альтернативы, если вы просто устанавливаете фиксированную точку на 2-байтовой границе, вы можете сделать что-то вроде:

void fixed_add( Fixed* a, Fixed *b, Fixed *c){
    int ia = a->floor << 16 + a->fraction;
    int ib = b->floor << 16 + b->fraction;
    int ic = ia + ib;
    c->floor = ic >> 16;
    c->fraction = ic - c->floor;
}
0 голосов
/ 26 октября 2010

Вы можете привести любой адресуемый тип к другому, используя:

*(newtype *)&var
0 голосов
/ 26 октября 2010
// add two Fixed 
Fixed operator+( Fixed a, Fixed b ) 
{   
...
}

//add Fixed and int
Fixed operator+( Fixed a, int b ) 
{   
...
}
0 голосов
/ 26 октября 2010

Если ваш компилятор помещает два коротких по 4 байта, то вы можете использовать memcpy для копирования вашего int в вашу структуру, но, как сказано в другом ответе, это не переносимо ... и довольно некрасиво.

Вы действительно хотите добавить отдельно каждое поле отдельным методом?Вы хотите сохранить целое число по соображениям производительности?

0 голосов
/ 26 октября 2010

Попробуйте это:

typedef union {
    struct Fixed_t {
        unsigned short floor; //left side of the decimal point
        unsigned short fraction; //right side of the decimal point
    } Fixed;
    int Fixed_int;
}
...