Как преобразовать список uint8 в соответствующее значение со знаком? - PullRequest
2 голосов
/ 13 июня 2019

Скажите, что у меня есть массив байтов:

std::array<std::uint8_t, 4> list

, и я хочу преобразовать их в соответствующее значение со знаком после объединения битов, содержащихся в list.Например, для случая list и поскольку это массив размера 4, это будет преобразовано в int32.Каков «правильный» способ сделать это в C ++, который не привел бы к неопределенному или специфическому поведению компилятора?Было бы правильно делать что-то подобное и не считаться неопределенным или специфичным для компилятора?

Ответы [ 2 ]

3 голосов
/ 13 июня 2019

Если вы настаиваете

, чтобы преобразовать их в соответствующее значение со знаком

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

#include <array>
#include <iostream>
#include <boost/numeric/conversion/cast.hpp>

#ifdef __linux__ 
    #include <arpa/inet.h>
#elif _WIN32
    #include <winsock.h>
#else
 // ...
#endif

int main()
{
    using boost::numeric_cast;
    using boost::numeric::bad_numeric_cast;
    using boost::numeric::positive_overflow;
    using boost::numeric::negative_overflow;

    std::array<uint8_t, 4> a = { 1,2,3,4 }; // big-endian
    uint32_t ui = ntohl(*reinterpret_cast<uint32_t*>(a.data())); // convert to host specific byte order
    std::cout << std::hex << ui << std::endl;

    try
    {
        int32_t si = numeric_cast<int32_t>(ui); // This conversion succeeds (is in range)
        std::cout << std::hex << si << std::endl;
    }
    catch (negative_overflow& e) {
        std::cout << e.what();
    }
    catch (positive_overflow& e) {
        std::cout << e.what();
    }
}
0 голосов
/ 13 июня 2019

Хорошо, но вы получите поведение, определенное реализацией, если значение без знака не может быть представлено в знаке (см., Например, this стандартный черновик c ++ онлайн):

4,7 Интегральные преобразования

3) Если тип назначения подписан, значение не изменяется, если оно может быть представленным в типе назначения (и ширине битового поля); в противном случае значение определяется реализацией.

Чтобы преодолеть это, вы можете ограничить старший байт до 7 бит:

sum = sum + (static_cast<std::uint32_t>(list[3]) & 0x7F) <<24;
if (static_cast<std::uint32_t>(list[3]) & 0x80) {
   sum = -sum;
}

Обратите внимание, что только 31 бит может быть использован для содержимого подписанного значения. На самом деле вы не можете полагаться на два представления дополнения, но с пометкой sum = -sum вы должны быть в безопасности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...