Преобразование float в 4 uint8_t
У меня есть переменная типа 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
...
Ответы
Ответ 1
Обычно вы делаете это, бросая float в массив uint8_t.
В C вы можете сделать это следующим образом:
uint8_t *array;
array = (unit8_t*)(&f);
в С++ используйте reinterpret_cast
uint8_t *array;
array = reinterpret_cast<uint8_t*>(&f);
Затем массив [0],..., array [3] - ваши байты.
Ответ 2
Сначала вы должны заметить, что стандарт не налагает никаких ограничений по размеру на 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.
Ответ 3
Здесь применяется подход объединения, который дает отдельные имена для целых частей вместо одного массива:
union {
float f;
struct {
uint8_t ut1, ut2, ut3, ut4;
} bytes;
} value;
value.f = 1.f;
uint8_t first = value.bytes.ut1;
Я изначально был обеспокоен тем, что это использование union
не является строго законным в соответствии со стандартом: С++ Undefined поведение с объединениями, но аргумент ComicSansMS в комментарии ответ на rashmatash является убедительным.
Ответ 4
Вы можете выполнить эту незаконную операцию:
float f = someFloatValue;
uint8_t* i = reinterpret_cast<uint8_t*>(&f);
Хотя это работает большую часть времени, оно не поддерживается стандартом С++, и компиляторы могут генерировать код с поведением undefined.
Другим решением является использование объединений:
union{
float f;
uint8_t i[4];
}
f = someFloatValue;
// now i contain the bit pattern of f
Неясно, дают ли все компиляторы согласованные результаты, но они кажутся более безопасными, чем первый aproach.
Вы также можете упаковать значение f
в 32-битное целое число. Это, однако, может привести к потере некоторой точности, но в зависимости от того, насколько точно вы хотите сохранить f
, было бы лучшим решением.