Как я могу добавить 64-разрядное число с плавающей запятой в массив символов без знака по определенным индексам (C )

#c #arrays #c #char #stdstring

#c #массивы #c #символ #stdstring

Вопрос:

Мне нужно добавить 64-разрядное число с плавающей запятой в unsigned char массив по определенным индексам (например 1 , через индекс 8 ).

Пример unsigned char массива:

 unsigned char msg[10] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  

Я хочу добавить число с плавающей запятой 0.084 , например, которое представлено 1B2FDD240681B53F в виде шестнадцатеричного (с маленьким порядковым номером), в массив символов без знака по индексам 1,2,3,4,5,6,7,8 и оставить индексы 0 и 9 неизменными.

Итак, я хотел бы, чтобы массив символов без знака, msg , содержал следующее:

 msg = {0x00, 0x1B, 0x2F, 0xDD, 0x24, 0x06, 0x81, 0xB5, 0x3F, 0x00}
  

Пока я могу получить std::string шестнадцатеричное представление примера значения с плавающей запятой 0.084 , используя следующий код, но я не уверен, как добавить строковые значения обратно в массив символов без знака:

 #include <iostream>
#include <sstream> 
#include <iomanip>

using namespace std;

int main()
{
    union udoub
    {
        double d;
        unsigned long long u;
    };

    double dVal = 0.084;
    udoub val;
    val.d = dVal;
    
    std::stringstream ss;
    ss << std::setw(16) << std::setfill('0') << std::hex << val.u << std::endl;
    std::string strValHexString = ss.str();
    
    cout<< strValHexString << std::endl;

    return 0;
}
  

Вывод:
3fb5810624dd2f1b

Я попытался использовать std::copy , как в примере ниже, для копирования значений из std::string в unsigned char , но, похоже, это не делает то, что я хочу:

 unsigned char ucTmp[2];
std::copy(strValHexString.substr(0,2).begin(), strValHexString.substr(0,2).end(), ucTmp);
  

Ищу решение на C или C .

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

1. @Adrian Mole: почему вы вообще думаете о рассмотрении чего-либо, кроме решения Adrian Mole?

2. @paulsm4 Зачем OP даже думать о рассмотрении сериализации в массив байтов, если они не выполняют хранение файлов или сетевую связь. В этих случаях решение Адриана Моула имеет проблему несовместимости между системами с разным порядковым номером. Это нормально, если оно ограничено одной системой.

3. Это для сетевого взаимодействия — изначально я думал, что мне придется изменить порядок, который я сделал перед использованием строк, но я считаю, что обе системы имеют небольшой конец, поэтому решение memcpy, похоже, работает нормально.

4. @user3716193 the memcpy solution seems to work fine Это может показаться так, если вы тестируете только на одной системе или системах с одинаковым порядковым номером. Это не будет работать между системами с разным порядковым номером, что обычно является возможностью, которую следует учитывать при сетевом взаимодействии.

5. Для автономного приложения беспокоиться о «порядковом порядке»… глупо. Однако для сетевого сообщения это, вероятно, ВАЖНО . Честно говоря, я не заметил, что имя переменной было «msg»; и при этом OP явно не сказал об этом в своем сообщении. «Сетевые сообщения» определенно требуют «более надежного» решения…

Ответ №1:

Форматирование байтов компонента в шестнадцатеричную строку, а затем повторное их чтение — ужасная трата времени и усилий. Просто используйте std::memcpy() (в C ) или memcpy (в C):

     std::memcpy(amp;msg[1], amp;dVal, sizeof(dVal));
  

Это решит все необходимые проблемы с выравниванием указателей. Однако это не будет выполнять никакой «интерпретации» с точки зрения вашего порядкового номера — но это не должно быть проблемой, если вы затем не передаете этот массив байтов между разными платформами.

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

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

Ответ №2:

Ваш пример имеет неопределенное поведение из-за чтения из неактивного члена объединения. Четко определенный способ преобразования в целое число:

 auto uVal = std::bit_cast<std::uint64_t>(dVal);
  

Теперь, когда у вас есть данные в виде целого числа, вы можете использовать побитовые операции для извлечения отдельных октетов в определенных позициях:

 msg[1] = (uVal >> 0x0 ) amp; 0xff;
msg[2] = (uVal >> 0x8 ) amp; 0xff;
msg[3] = (uVal >> 0x10) amp; 0xff;
msg[4] = (uVal >> 0x18) amp; 0xff;
msg[5] = (uVal >> 0x20) amp; 0xff;
...
  

Это можно сжать в цикл.

Обратите внимание, что это работает одинаково независимо от порядкового номера процессора. Результирующий порядок в массиве всегда будет иметь малый порядковый номер, в отличие от прямого std::memcpy подхода, который приводит к собственному порядковому порядку, который не обязательно имеет малый порядковый номер во всех системах. Однако, если числа с плавающей запятой и целые числа используют разный порядковый номер, то порядок не будет одинаковым даже при таком подходе.

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

1. std::bit_cast недоступен для меня, я использую C 11.

2. @user3716193 Вы можете написать свое собственное not_std::bit_cast использование std::memcpy и использовать его вместо этого.

3. В итоге я использовал std::memcpy. Поддержал этот ответ, хотя — я думаю, что это было направление, в котором я хотел пойти изначально — возможность использовать побитовые операции, потому что я использовал их раньше с целыми числами, я просто не мог понять, как это сделать с числами с плавающей запятой. Спасибо.