-1

I have a std::vector<unsigned char> containing values representing the bytes coming from the network. I want to interpret every 8 elements as a double, similar to this but for extracting a double instead of a uint32_t:

uint32_t extractUint32From(vector<unsigned char> const& from, uint32_t startIndex)
{
    uint32_t value = 0;
    for (int i = 0; i < sizeof(uint32_t); i++)
    {
        value |= from[startIndex + i] << (i * 8);
    }
    return value;
}

I have tried:

double extractDoubleFrom(vector<unsigned char> const& from, uint32_t startIndex){
    uint64_t d;
    for (int i = 0; i < sizeof(double); i++)
    {
        //std::cout << (int)from[startIndex + i] << "\n";
        d |= from[startIndex + i] << (i*8); 
    }
    //std::cout << d << "\n";
    return static_cast<double>(d);
}

And some other variations... is there something I am doing wrong?

EDIT:

The following solution worked for me:

double d;
std::vector<unsigned char> test = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f}; // this is 1.5
memcpy(test.data(), &d, sizeof(double));
std::cout << d; 
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    Do you know what `reinterpret_cast` is, and how to use it? – Sam Varshavchik Aug 16 '22 at 15:30
  • @SamVarshavchik The classic `reinterpret_cast`-based solution suffers from enforcing a particular endianness. – lorro Aug 16 '22 at 15:35
  • 3
    @SamVarshavchik [using `reinterpret_cast` for type punning invokes UB](https://stackoverflow.com/q/53995657/995714). You must use `std::bit_cast` or `memcpy` for that purpose – phuclv Aug 16 '22 at 15:36
  • 1
    Eh don't do it yourself? And use protobuf + gRPC (https://grpc.io/docs/what-is-grpc/introduction/). It will save you a lot of trouble (e.g. endianess differences between machines) – Pepijn Kramer Aug 16 '22 at 15:36
  • 2
    You can just use `std::memcpy`. [demo](https://godbolt.org/z/vvqYerxG5) with some garbage values as example – pptaszni Aug 16 '22 at 15:36
  • @SamVarshavchik not good enugh, i am still trying to understand it i have also tried reintepret_cast in the above example as follows: return *reinterpret_cast(&d) – ElectronVolt Aug 16 '22 at 15:52
  • No, that's not how `reinterpret_cast` should be used here. – Sam Varshavchik Aug 16 '22 at 15:52
  • @pptaszni thanx for the demo, i have changed it a little. would be thankful if you could look over it. https://godbolt.org/z/36Gj8jzWh – ElectronVolt Aug 16 '22 at 16:27
  • Yeah, because you cannot just take the address of `std::vector`, you need to call `std::vector::data` method to get internal array. Here you go: https://godbolt.org/z/5WThxEGnT – pptaszni Aug 16 '22 at 18:55
  • @ElectronVolt your *edit* should have been posted as an *answer* instead. See [Can I answer my own queestion?](https://stackoverflow.com/help/self-answer) – Remy Lebeau Aug 16 '22 at 23:54

1 Answers1

0

The 'byte order' in which it is sent from the network is important. (Big or Little endian Endianness)

Hopefully, the example below may help you find a solution to your issue.

// CppConsoleApplication1.cpp
//

#include <stdio.h>
#include <tchar.h>
#include <stdint.h>
#include <assert.h>
#include <vector>

#define MAX_DOUBLE      1.7976931348623157E+308
#define DOUBLE_SIZE     ((int)sizeof(double))

int GetPlatformByteOrder(void);

const int LITTLE_ENDIAN = 1234;
const int BIG_ENDIAN    = 4321;
const int BYTE_ORDER    = GetPlatformByteOrder();

int GetPlatformByteOrder(void)
{
    const unsigned int cbuffer = sizeof(unsigned int);
    unsigned int value = 0x0A0B0C0D;
    unsigned char buffer[cbuffer];
    memcpy_s((void*)buffer, cbuffer, (const void*)&value, cbuffer);
    return (buffer[0] == 0x0A) ? BIG_ENDIAN : LITTLE_ENDIAN;
}

double ToDouble(const std::vector<unsigned char>& from, uint32_t startIndex, int byteOrder = BYTE_ORDER)
{
    assert((from.size() - startIndex) >= DOUBLE_SIZE);

    double retVal = 0;
    unsigned char* p = (unsigned char*)&retVal;

    if (byteOrder != BYTE_ORDER) {
        for (int32_t i = 0; i < DOUBLE_SIZE; i++) {
            p[i] = from[(DOUBLE_SIZE - (i + 1)) + startIndex];
        }
    }
    else {
        for (int32_t i = 0; i < DOUBLE_SIZE; i++) {
            p[i] = from[i + startIndex];
        }
    }

    return retVal;
}

std::vector<unsigned char> ToArray(double value, int byteOrder = BYTE_ORDER)
{
    std::vector<unsigned char> retVal;
    unsigned char* p = (unsigned char*)&value;

    if (byteOrder != BYTE_ORDER) {
        for (int32_t i = 0; i < (int32_t)DOUBLE_SIZE; i++) {
            retVal.push_back(p[DOUBLE_SIZE - (i + 1)]);
        }
    }
    else {
        for (int32_t i = 0; i < (int32_t)DOUBLE_SIZE; i++) {
            retVal.push_back(p[i]);
        }
    }

    return retVal;
}

int _tmain(int argc, _TCHAR* argv[])
{
    double value0 = MAX_DOUBLE;
    double value1;
    std::vector<unsigned char> datas;

    // Test1
    datas = ToArray(value0);
    value1 = ToDouble(datas, 0);
    printf("Before: %1.16E\n", value0);
    printf("After : %1.16E\n", value1);
    printf("Result: %s\n", value1 == value0 ? "Correct" : "Incorrect");
    printf("\n");
    //assert(value1 == value0);

    // Test2
    datas = ToArray(value0, BIG_ENDIAN);
    value1 = ToDouble(datas, 0, BIG_ENDIAN);
    printf("Before: %1.16E\n", value0);
    printf("After : %1.16E\n", value1);
    printf("Result: %s\n", value1 == value0 ? "Correct" : "Incorrect");
    printf("\n");
    //assert(value1 == value0);

    // Test3 (with error)
    // For example, We don't know the byte order of the source.
    datas = ToArray(value0, BIG_ENDIAN);
    // For example, we assume the byte order of the source is LITTLE_ENDIAN.
    value1 = ToDouble(datas, 0, LITTLE_ENDIAN); // ???
    printf("Before: %1.16E\n", value0);
    printf("After : %1.16E\n", value1);
    printf("Result: %s\n", value1 == value0 ? "Correct" : "Incorrect");
    printf("\n");
    //assert(value1 == value0);

    printf("\nPress enter to exit.");
    getchar();
    return 0;
}

The result will be as shown below.


    Before: 1.7976931348623157E+308
    After : 1.7976931348623157E+308
    Result: Correct
    
    Before: 1.7976931348623157E+308
    After : 1.7976931348623157E+308
    Result: Correct
    
    Before: 1.7976931348623157E+308
    After : -1.#QNAN00000000000E+000
    Result: Incorrect

gerdogdu
  • 44
  • 5