c++ - How to convert a byte array into double in C? - Stack Overflow
c++ - Convert between double and byte array, for transfer over ZigBee API? - Stack Overflow
c++ double to byte array - Stack Overflow
c - Set a double to byte array - Stack Overflow
Try this:
double a;
memcpy(&a, ptr, sizeof(double));
where ptr is the pointer to your byte array. If you want to avoid copying use a union, e.g.
union {
double d;
char bytes[sizeof(double)];
} u;
// Store your data in u.bytes
// Use floating point number from u.d
Here is one solution using memcpy function:
double d = 0;
unsigned char buf[sizeof d] = {0};
memcpy(&d, buf, sizeof d);
Note that a solution like:
d = *((double *) buf);
shoud be avoided. This is undefined behavior because it potentially violates alignment and aliasing rules.
What you're doing is called type punning.
Use a union:
union {
double d[2];
char b[sizeof(double) * 2];
};
Or use reinterpret_cast:
char* b = reinterpret_cast<char*>(d);
Here's a rather unsafe way to do it:
double d = 0.123;
char *byteArray = (char*)&d;
// we now have our 8 bytes
double final = *((double*)byteArray);
std::cout << final; // or whatever
Or you could use a reinterpret_cast:
double d = 0.123;
char* byteArray = reinterpret_cast<char*>(&d);
// we now have our 8 bytes
double final = *reinterpret_cast<double*>(byteArray);
std::cout << final; // or whatever
std::vector<unsigned char*> is a vector (list) of pointers to unsigned char. This is not what you want.
I guess you want to have std::vector<unsigned char> (no pointers here).
For each number you want to add to the list, you have to add several (typically 8) bytes to it. In order to do it, the simplest method is:
const unsigned char* ptr = reinterpret_cast<const unsigned char*>(&a);
for (size_t i = 0; i < sizeof(double); ++i)
v.push_back(ptr[i]);
To extract a number from such an array of bytes:
for (int i = 0; i < v.size(); i++) {
double number;
memcpy(&number, &v.at(i * sizeof(double)), sizeof(double));
std::cout << number << std::endl;
}
This is really wrong you are saving pointers to address that would be out of scope (a and b), this would cause invalid memory access (would work as you expected OR would not).
You are declaring a vector of pointer (std::vector<unsigned char*>) not a vector of unsigned char (std::vector<unsigned char>).
According to the output of this sample, it appear as if GCC (version 4.9.0) it reusing the same memory for variables a and b in the loop OR extracted away the variables of the loop. Because it prints only two different address of the pointer storage in the vector.
#include <vector>
#include <iostream>
int main() {
std::vector<unsigned char*> v;
for (int i = 0; i < 5; i++) {
double a = 3.14 + i;
double b = 4.44 + i;
v.push_back((unsigned char*)&a);
v.push_back((unsigned char*)&b);
}
for (int i = 0; i < v.size(); i++) {
std::cout << std::hex << ((double*)v.at(i)) << std::dec << std::endl;
std::cout << *((double*)v.at(i)) << std::endl;
}
}
Output:
0x22fec0 -> Address 1
7.14
0x22fec8 -> Address 2
8.44
0x22fec0 -> Address 1
7.14
0x22fec8 -> Address 2
8.44
0x22fec0 -> Address 1
7.14
0x22fec8 -> Address 2
8.44
0x22fec0 -> Address 1
7.14
0x22fec8 -> Address 2
8.44
0x22fec0 -> Address 1
7.14
0x22fec8 -> Address 2
8.44
If your compiler isn't horribly broken, copy through assignment operator should be more or less identical to memcpy. So what you are trying is nothing but "pre-mature optimization".
You can't write code like *pdest = (double)p->stk[stkpos]; because that invokes undefined behavior. See What is the strict aliasing rule? memcpy however, doesn't come with that bug.
Solution: use memcpy.
Assuming p->strk is an array of bytes:
This doesn't work:
*pdest = (double)p->stk[stkpos];
Because you're only reading a single byte and assigning that value to a double.
This doesn't work:
*pdest = (double)((double*)&p->stk[stkpos]);
Because of the outer cast. The first part, casing &p->stk[stkpos] to a double * will in fact give you a pointer to a double that starts at the address of &p->stk[stkpos], but then you cast the value of that pointer, i.e. a memory address (not what it points to) to a double which doesn't make sense.
What you were probably trying to do what this:
*pdest = *((double*)&p->stk[stkpos]);
This takes the address of p->stk[stkpos], treats it as a pointer to a double, and dereferences the address to read a double.
There's still a problem with this, however. Such a conversion violates strict aliasing. And even if strict aliasing was disabled, you might end up with a misaligned memory access.
The proper way to do this is to use memcpy. That is guaranteed to work as expected.
Easiest is to make a union:
#include <stdio.h>
int main(void) {
int ii;
union {
float a;
unsigned char bytes[4];
} thing;
thing.a = 1.234;
for (ii=0; ii<4; ii++)
printf ("byte %d is %02x\n", ii, thing.bytes[ii]);
return 0;
}
Output:
byte 0 is b6
byte 1 is f3
byte 2 is 9d
byte 3 is 3f
Note - there is no guarantee about the byte order… it depends on your machine architecture.
To get your function to work, do this:
void float2Bytes(byte bytes_temp[4],float float_variable){
union {
float a;
unsigned char bytes[4];
} thing;
thing.a = float_variable;
memcpy(bytes_temp, thing.bytes, 4);
}
Or to really hack it:
void float2Bytes(byte bytes_temp[4],float float_variable){
memcpy(bytes_temp, (unsigned char*) (&float_variable), 4);
}
Note - in either case I make sure to copy the data to the location given as the input parameter. This is crucial, as local variables will not exist after you return (although you could declare them static, but let's not teach you bad habits. What if the function gets called again…)
Here's a way to do what you want that won't break if you're on a system with a different endianness from the one you're on now:
byte* floatToByteArray(float f) {
byte* ret = malloc(4 * sizeof(byte));
unsigned int asInt = *((int*)&f);
int i;
for (i = 0; i < 4; i++) {
ret[i] = (asInt >> 8 * i) & 0xFF;
}
return ret;
}
You can see it in action here: http://ideone.com/umY1bB
The issue with the above answers is that they rely on the underlying representation of floats: C makes no guarantee that the most significant byte will be "first" in memory. The standard allows the underlying system to implement floats however it feels like -- so if you test your code on a system with a particular kind of endianness (byte order for numeric types in memory), it will stop working depending on the kind of processor you're running it on.
That's a really nasty, hard-to-fix bug and you should avoid it if at all possible.
Building a float or double character-by-character can cause a trap representation so don't do that. Replace the _arr definition with std::array<char, Size> _arr; and then use in.read + std::memcpy:
Example:
#include <cstring> // std::memcpy
// writer:
std::array<char, Size> _arr;
bool convert_num(T inp) {
static_assert(sizeof(T) == sizeof(value_t),
"T and value_t need to be the same size");
std::memcpy(_arr.data(), &inp, Size);
return true;
}
bool write(std::ostream& outfile) {
return static_cast<bool>(outfile.write(_arr.data(), Size));
}
// reader:
std::array<char, Size> _arr;
bool add_nums(std::istream& in) {
return static_cast<bool>(in.read(_arr.data(), Size));
}
bool convert() {
std::memcpy(&outp, _arr.data(), Size);
return true;
}
Demo (originally from Paddy)
The main issue with your program is the shifting of a smaller data type when you read. You have this in two places (shown together here for clarity, despite being out of context):
outp += ((uint8_t)(*begin) << (i * 8));
outp += ((*begin) << (i << 3));
In both these cases, the iterator begin references an underlying type of int8_t. Not only is this a signed type, but you're placing trust in the compiler to promote the type to a large enough integer. I actually ran into a similar issue some years ago.
See here: Undefined high-order of uint64_t while shifting and masking 32-bit values
The fix is to cast to the correct size before shifting:
outp += (((value_t)(uint8_t)*begin) << (i * 8));
outp += (((value_t)(uint8_t)*begin) << (i << 3));
I would also like to suggest that you avoid using constructs like i << 3 instead of i * 8. There is simply no need to try and be clever. The compiler will do the right thing, and you're just making the code harder to read. It also lacks consistency, since you're using both forms in the same function.