При разработке программного обеспечения для микроконтроллеров довольно часто приходится преобразовывать данные из типа float в массив из четырех байт и обратно. Если использовать для этого тип union, то такая задача решается элементарно.
- Тип float в языках C и C++
- Тип union в языках C и C++
- Преобразование из float в char[4] и обратно в C и C++
Тип float в языках C и C++
Начнем с типа данных float в языках C и C++. Этот тип используется для хранения вещественных чисел с плавающей точкой в формате IEEE 754. Диапазон возможных значений: [1,2E-38; 3,4E+38], точность – шесть знаков. Если мы оперируем целым числом в формате int, long и т.п., запаковать его в нужное количество байт не составит труда, для этого даже не нужно использовать специальные средства языка. Ниже представлен пример на C++.
#include <iostream> using namespace std; int main() { long val = 89564732449; unsigned char arr[4] = { 0, 0, 0, 0 }; arr[0] = val & 0xff; arr[1] = (val >> 8) & 0xff; arr[2] = (val >> 16) & 0xff; arr[3] = (val >> 24) & 0xff; cout << std::hex << (int)arr[0] << " " << (int)arr[1] << " " << (int)arr[2] << " " << (int)arr[3] << endl; return 0; }
В результате появится следующая строка:
21 5c 79 da
Это исходные целое число, разложенное по байтам и представленное в шестнадцатеричном формате. Для начального знакомства с форматом IEEE 754 предлагаю прочитать вот эту статью. Формат числа выглядит так.
И, как вы можете видеть, просто так расколоть его на байты уже не получится. Но с этим отлично справляется тип union.
Тип union в языках C и C++
Тип union – это специальный тип данных, позволяющий хранить переменные различных типов данных в одной и той же области памяти. В C и C++ синтаксис немного отличается. В C++ использование union выглядит так.
#include <iostream> using namespace std; int main() { union DataType { unsigned long longVal; unsigned short shortVal[2]; }; DataType sample; sample.longVal = 45265795; cout << "long value: " << sample.longVal << endl; cout << "two short values: " << sample.shortVal[0] << " " << sample.shortVal[1] << endl; return 0; }
Получим следующий результат.
long value: 2b2b383
two short values: b383 2b2
Реализуем этот же функционал на языке C.
#include <stdio.h> int main() { union DataType { unsigned long longVal; unsigned short shortVal[2]; }; union DataType sample; sample.longVal = 45265795; printf("long value: %x\n", sample.longVal); printf("two short values: %x %x\n", sample.shortVal[0], sample.shortVal[1]); return 0; }
Как видно из представленных примеров: одна и та же область памяти (четыре байта) представляется с одной стороны как число типа unsigned long, с другой как два числа типа unsigned short.
Преобразование из float в char[4] и обратно в C и C++
Используем изложенный выше подход для запаковки float в четыре байта с возможностью распаковки.
Реализуем это на языке C++.
#include <iostream> using namespace std; int main() { union DataType { float fVal; unsigned char buf[4]; }; DataType sample; sample.fVal = 123.98; cout << "float value: " << sample.fVal << endl; cout << std::hex << "buffer: " << (int)sample.buf[0] << " " << (int)sample.buf[1] << " " << (int)sample.buf[2] << " " << (int)sample.buf[3] << endl; system("pause"); return 0; }
Массив sample.buf можно передавать по сети (например: Modbus), а также принимать в него входные данные, содержащие значение типа float. Конечно, можно и вообще без union обойтись и сделать следующим образом.
float fVal = 123.98; cout << std::hex << "buffer: " << ((int)(*((char*)&fVal)) & 0xff) << " " << ((int)(*((char*)&fVal + 1)) & 0xff) << " " << ((int)(*((char*)&fVal + 2)) & 0xff) << " " << ((int)(*((char*)&fVal + 3)) & 0xff) << endl;
Но это менее интуитивно)).