Как получить байты float?

Я использую HIDAPI для отправки некоторых данных на USB-устройство. Эти данные могут быть отправлены только как массив байтов, и мне нужно отправить некоторые номера float внутри этого массива данных. Я знаю, что поплавки имеют 4 байта, поэтому я подумал, что это может сработать:

float f = 0.6;
char data[4];

data[0] = (int) f >> 24;
data[1] = (int) f >> 16;
data[2] = (int) f >> 8;
data[3] = (int) f;

и позже все, что мне нужно было сделать, это:

g = (float)((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]) );

но тестирование этого показывает мне, что строки, как data[0] = (int) f >> 24; возвращает всегда 0. Что не так с моим кодом и как я могу сделать это правильно (т. е. сломать внутренние данные float в 4 байтах char и перестроить тот же поплавок позже)?

EDIT:

я смог выполнить это со следующими кодами:

float f = 0.1;
unsigned char *pc;
pc = (unsigned char*)&f;

// 0.6 in float
pc[0] = 0x9A;
pc[1] = 0x99;
pc[2] = 0x19;
pc[3] = 0x3F;

std::cout << f << std::endl; // will print 0.6

и

*(unsigned int*)&f = (0x3F << 24) | (0x19 << 16) | (0x99 << 8) | (0x9A << 0);

Я знаю, что memcpy () - это" более чистый " способ сделать это, но таким образом я думаю, что производительность несколько лучше.

6 ответов


вы можете сделать это так:

char data[sizeof(float)];


float f = 0.6f;

memcpy(data, &f, sizeof f);    // send data


float g;

memcpy(&g, data, sizeof g);    // receive data

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


как было справедливо указано в комментариях, вам не обязательно делать дополнительные memcpy; вместо этого, вы можете лечить f напрямую как массив символов (любой значимости). Вы все еще должны сделать memcpy на принимающей стороне, хотя, так как вы можете не лечения произвольный массив символов как float! Пример:

unsigned char const * const p = (unsigned char const *)&f;
for (size_t i = 0; i != sizeof f; ++i)
{
    printf("Byte %zu is %02X\n", i, p[i]);
    send_over_network(p[i]);
}

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

 #include <stdio.h> 

 int main(void)
 {
    float x = 0x1.0p-3; /* 2^(-3) in hexa */

    union float_bytes {
       float val;
       unsigned char bytes[sizeof(float)];
    } data;

    data.val = x;
    for (int i = 0; i < sizeof(float); i++) 
          printf("Byte %d: %.2x\n", i, data.bytes[i]);

    data.val *= 2;   /* Doing something with the float value */
    x = data.val;    /* Retrieving the float value           */
    printf("%.4f\n", data.val);

    getchar();
 }

как вы можете видеть, совсем не обязательно использовать memcpy или указатели...

на union подход легко понять, стандартный и быстрый.

правка.

я объясню, почему этот подход действителен в C (C99).

  • [5.2.4.2.1(1)] в байт CHAR_BIT bits (целочисленная константа >= 8, почти в случаях 8).
  • [6.2.6.1(3)] на unsigned char type использует все свои биты для представления значения объекта, который является неотрицательным целым числом, в чистом двоичном представлении. Это означает, что нет бит заполнения или бит, используемых для любого другого extrange purpouse. (То же самое не гарантируется для signed char или char типы).
  • [6.2.6.1(2)] в поле тип представлен в памяти в виде непрерывной последовательности байтов.
  • [6.2.6.1(4)] (цитируется) "значения, хранящиеся в объектах без битового поля любого другого типа объектов, состоят из N × char_bit бит, где n-размер объекта этого типа, в байтах. Значение может быть скопировано в объект типа unsigned char [n] (например, memcpy); [...]"
  • [6.7.2.1(14)] указатель на a объект структуры (в частности, объединения), соответствующим образом преобразованный, указывает на его исходный член. (Таким образом, в начале объединения нет байтов заполнения).
  • [6.5(7)] к содержимому объекта можно получить доступ с помощью символьного типа:

объект должен иметь свое сохраненное значение, доступ только к выражению lvalue, которое имеет одно из следующие типы:
- тип, совместимый с действующим тип объект,
- квалифицированная версия типа, совместимого с эффективным типом объекта,
- тип, который является подписанным или неподписанным типом, соответствующим действующему типу объект,
- тип, который является знаковым или беззнаковым типом, соответствующим квалифицированным версия эффективный тип объекта,
- тип агрегата или объединения, который включает один из вышеупомянутых типов среди своих члены (включая, рекурсивно, amember субагрегата или contained union), или
- тип характера

дополнительная информация:

обсуждение в группах google
Type-punning

Изменить 2

еще одна деталь стандарта C99:

  • [6.5.2.3 (3) сноска 82] Type-punning разрешается:

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


язык C гарантирует, что любое значение любого type1 может быть доступно в виде массива байтов. Тип байт unsigned char. Вот низкоуровневый способ копирования float в массив байтов. sizeof(f) - количество байтов, используемых для хранения значения переменной f; вы можете также использовать sizeof(float) (вы можете либо передать sizeof переменной или более сложное выражение, или его тип).

float f = 0.6;
unsigned char data[sizeof(float)];
size_t i;
for (i = 0; i < sizeof(float); i++) {
    data[i] = (unsigned char*)f + i;
}

функции memcpy или memmove сделать именно это (или оптимизированную версию из этого.)

float f = 0.6;
unsigned char data[sizeof(float)];
memcpy(data, f, sizeof(f));

вам даже не нужно делать эту копию. Вы можете напрямую передать указатель на float в функцию записи на USB и указать, сколько байтов нужно скопировать (sizeof(f)). Вам понадобится явное приведение, если функция принимает аргумент указателя, отличный от void*.

int write_to_usb(unsigned char *ptr, size_t size);
result = write_to_usb((unsigned char*)f, sizeof(f))

отметим, что это будет работать только если устройство использует то же представление чисел с плавающей запятой, который является общим, но не универсальным. Большинств машины используют форматы с плавающей запятой IEEE, но вам может потребоваться переключить endianness.


а что с твоей попытки:>> оператор работает на целых числах. В выражении (int) f >> 24, f бросается в int; если бы вы писали f >> 24 без приведения, f все равно будет автоматически преобразован в int. Преобразование значения с плавающей запятой в целое число аппроксимирует его путем усечения или округления (обычно в направлении 0, но правило зависит от платформы). 0.6 округлено до целого числа 0 или 1, поэтому data[0] равно 0 или 1, а остальные-0.

вам нужно действовать на байты объекта float, а не на его значение.

1 исключая функции, которыми на самом деле нельзя манипулировать в C, но включая указатели функций, функции которых распадаются автоматически.


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

unsigned char payload[4];
memcpy(payload, &f, 4);

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

hostPort writes char * "34.56" byte by byte
client reads char * "34.56" 

затем преобразуется в float с функцией библиотеки atof или atof_l.

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

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

34.56 становится char array[] = {4,-2,34,56}; что-то вроде этого было бы портативным... Я бы просто попытался не передавать двоичные представления float... потому что это может стать грязным быстро.


было бы безопаснее объединить массив float и char. Поместите в элемент float, вытащите 4 (или любую длину) байта.