Упаковка плавает в длинный длинный - PullRequest
2 голосов
/ 10 января 2011

Я бы хотел упаковать 2 поплавка в длинный. Как правильно это сделать?

Ответы [ 5 ]

5 голосов
/ 10 января 2011

Слегка зависит от того, насколько вы хотите его видеть, в двух смыслах: на каких платформах он вообще работает и на каких платформах он дает одинаковый ответ. Но как то так.

#include <stdint.h>

long long pack(float lo, float hi) {
    assert(sizeof(float) == 4);
    assert(sizeof(long long) == 8);
    assert(CHAR_BIT == 8);

    uint32_t target;
    memcpy(&target, &lo, sizeof(target));
    long long result = target;
    memcpy(&target, &hi, sizeof(target));
    result += ((long long)target) << 32;
    return result;
}

«Другой» способ получить биты с плавающей точкой в ​​виде целого числа в одной записи + чтение - это так называемое «объединение типов», но я предпочитаю memcpy. Вы также можете получить доступ к поплавку по одному unsigned char за раз и создать long long с 8-битными сменами.

3 голосов
/ 10 января 2011

Предполагая, что ваши числа с плавающей запятой представлены 32-битными, в общем случае вы не сможете сделать это без потери данных.

Если вам абсолютно необходимо это сделать, вы можете преобразовать их каждый в 16-разрядное число с плавающей запятой (он же «половина») http://en.wikipedia.org/wiki/Half_precision_floating-point_format

Страница википедии содержит ссылку (в самом низу), которая предоставляет информацию о том, как выполнить преобразование в C.

Когда у вас есть «половинки», вы можете затем добавить битовую маску и сдвинуть их в 32-разрядное целое число.

Обновление , теперь этот вопрос был заменен на 'long long' вместо uint_32:

Было упомянуто решение memcpy, вот пример использования объединения:

#include <stdio.h>
#include <stdint.h>

typedef union
{
    uint64_t llv;
    float fv[2];
} conv ;


int main(int argc, char **argv)
{
    conv a;
    conv b;

    a.fv[0] = 1.0;
    a.fv[1] = -2.0;

    printf("%f %f\n",a.fv[0],a.fv[1]);
    b.llv = a.llv;
    printf("%f %f\n",b.fv[0],b.fv[1]);
}
3 голосов
/ 10 января 2011

Вы не можете сделать это. Число с плавающей запятой шириной 32 бита, поэтому в uint32_t.

помещается только один объект с плавающей запятой.

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

#include <stdint.h>

union converter { uint32_t i; float f; };

uint64_t pack(float a, float b) {
    union converter ca = { .f = a };
    union converter cb = { .f = b };
    return ((uint64_t)cb.i << 32) + ca.i;
}

void unpack(uint64_t packed, float *a, float *b) {
    union converter ca = { .i = packed };
    union converter cb = { .i = packed >> 32 };
    *a = ca.f;
    *b = cb.f;
}

Примечание: используйте [u]int64_t, а не long long; нет никакой гарантии, что long long - это точно 64 бита.

2 голосов
/ 10 января 2011

Попробуйте следующее:

    void *float1_bits = &float1;
    void *float2_bits = &float2;
    long long packed_floats = (*(long long*)float2_bits << 32) | (*(long long *)float1_bits);
1 голос
/ 10 января 2011

Стандартные значения типа float и double равны 4 и 8 байтов, поэтому вы не можете напрямую упаковать два из них в один объект типа int 32 , поскольку он составляет всего 4 байта.

Однако вы можете определить 16-битный формат с ограниченной точностью и диапазоном экспоненты. (Я не проверял, был ли уже стандарт. Там, вероятно, есть, но это веселее.)

f e d c|b a 9 8|7 6 5 4|3 2 1 0  
S E E E|E E m m|m m m m|m m m m

Мой новый формат, названный Ross-float, имеет знаковый бит, 5 битов показателя степени и 10 битов точности.

Этот формат может представлять целые числа в точности от -1023 до +1023, и он может представлять действительные числа в диапазоне от 10 -4,8 до 10 4,8 .

Обновление:

Хорошо, теперь вопрос изменился на long long , поэтому вся проблема становится тривиальной. Это также хорошо, потому что 16 бит недостаточно для хорошего числа с плавающей запятой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...