у меня есть float
переменная что мне нужно отправить через протокол CAN. Для этого необходимо срезать 32-битное число с плавающей запятой. 4 uint8_t
переменные.
Я понятия не имею, как это сделать. Сначала я думал о преобразовании числа с плавающей точкой в int, но некоторые ответы, которые я нашел в Интернете, которые используют приведение или объединение, похоже, не работают.
Вот простой пример того, что я пытаюсь сделать:
float f;
uint8_t ut1,ut2,ut3,ut4;
//8 first bits of f into ut1
//8 second bits of f in ut2
...
// Then I can send the uint8_t through CAN
...
Благодарю.
Обычно вы делаете это, приводя float к массиву uint8_t.
В C вы можете сделать это так:
uint8_t *array;
array = (unit8_t*)(&f);
в C ++ используйте reinterpret_cast
uint8_t *array;
array = reinterpret_cast<uint8_t*>(&f);
Тогда array [0], …, array [3] это ваши байты.
Во-первых, вы должны заметить, что стандарт не накладывает особых ограничений на размер float
, Возможно, что float
не вписался бы в четыре байта на какой-нибудь мыслимой архитектуре (хотя я не знаю ни одного). Вы должны по крайней мере (static_) утверждать, что он подойдет, прежде чем пытаться что-либо
Тогда я думаю, что самый простой способ — это CHAR_BIT
является 8
и использовать правовой псевдоним для unsigned char*
с reinterpret_cast
:
static_assert(sizeof(float) == 4);
float f = 0; // whatever value
unsigned char* float_as_char = reinterpret_cast<unsigned char*>(&f);
Это полностью игнорирует проблему с прямым порядком байтов, поэтому, возможно, вам действительно нужно сделать копию байтов, чтобы вы могли это исправить:
static_assert(sizeof(float) == 4);
float f = 0; // whatever value
uint8_t bytes[4];
std::memcpy(bytes, &f);
// Fix up the order of the bytes in "bytes" now.
Вы можете сделать эту незаконную операцию:
float f = someFloatValue;
uint8_t* i = reinterpret_cast<uint8_t*>(&f);
Хотя это работает большую часть времени, оно не поддерживается стандартом c ++, и компиляторы могут генерировать код с неопределенным поведением.
Другое решение использует союзы:
union{
float f;
uint8_t i[4];
}
f = someFloatValue;
// now i's contain the bit pattern of f
Неясно, дают ли все компиляторы согласованные результаты, но это кажется более безопасным, чем первый подход.
Вы также можете упаковать значение f
в 32-разрядном целом числе. Это, однако, может привести к потере точности, но в зависимости от того, насколько точно вы хотите сохранить f
было бы лучшим решением.
Вот объединенный подход, который дает отдельные имена для целочисленных частей вместо одного массива:
union {
float f;
struct {
uint8_t ut1, ut2, ut3, ut4;
} bytes;
} value;
value.f = 1.f;
uint8_t first = value.bytes.ut1;
Я был изначально обеспокоен тем, что это использование union
не является строго законным в соответствии со стандартом: C ++ Неопределенное поведение с объединениями но аргумент ComicSansMS в комментарии к ответу Рашматаша является убедительным.