Преобразование ответа MODBUS TCP в C с плавающей запятой

#c #tcp #hex #double #modbus

#c #tcp #шестнадцатеричный #двойной #modbus

Вопрос:

У меня проблема. У меня есть ответ MODBUS TCP, который имеет шестнадцатеричную кодировку следующим образом: 0 0 0 0 0 7 1 4 4 41 В8 66 64. Для пояснения: первые пять нулей являются спецификацией Modbus, 7 — это количество байтов, следующих за ним. 1 — это адрес клиента в сети Modbus и не имеет значения. первые 4 — это код функции, который используется. вторые 4 — это снова следующие числа байтов. Последние четыре байта — это шестнадцатеричный ответ, который должен быть преобразован в double. Массив, в котором это хранится, представляет собой массив символов без знака. Вот несколько примеров того, как я это пробовал. Вот первый пример:

 value = (ibuf[9]<<24)   (ibuf[10]<<16)   (ibuf[11]<<8)   ibuf[12];
  

Значение — это используемая двойная переменная, а ibuf — используемый массив символов.
Здесь второй:

 for(i = 0; i < k; i  )
{
    if (i==0)
    {
        sprintf(ergebnis, "%x%x", ibuf[9], ibuf[10]);
    }
    else
    {
        //sprintf(buffer,"%x%x",ibuf[9 i i], ibuf[10 i i]);
        //strcat( ergebnis, buffer );
        sprintf(ergebnis, "0x41b451e8");
        sscanf(ergebnis, "%l %lf ", amp;Value);
    }
    printf("Ergebnis %sn", ergebnis);
}
  

Здесь я использовал фиксированное значение, но проблема всегда заключается в преобразовании из шестнадцатеричного в double.
Я рад любой помощи, которую я мог получить.

Ответ №1:

Вы почти на месте. К сожалению, преобразование происходит автоматически во время продвижения — самый «простой» способ обойти это — разыменование приведения указателя с плавающей запятой для int , как так:

 int ival = (ibuf[9]<<24) (ibuf[10]<<16) (ibuf[11]<<8) ibuf[12];
float fval = *(float*)amp;ival;
  

Для предоставленных данных это дает 23.049995 вместо 1.1026039e 009, который вы получаете в результате преобразования.

Редактировать:

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

 float fval = *reinterpret_cast<float*>(amp;ival);
  

Обратите внимание, что вы не можете этого сделать:

 float fval = reinterpret_cast<float>(ival);
  

Это вызовет ошибку (как показано здесь в VS2005):

 error C2440: 'reinterpret_cast' : cannot convert from 'int' to 'float'
  

Комментарии:

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

Ответ №2:

Я бы использовал побитовый оператор or для воссоздания значения с плавающей запятой.

 unsigned int value = 0x00000000 | ibuf[9]  << 24
                                | ibuf[10] << 16
                                | ibuf[11] << 8
                                | ibuf[12];
float floatValue = (float)value;
  

Этот код предполагает, что unsigned int имеет размер четыре байта на вашей платформе.

Комментарии:

1. прямое преобразование в виде float выполняет преобразование. Этот метод работает некорректно. Смотрите мой ответ.