How to create a structure from 2 uint to double

I have a structure as shown below:

struct pts_t { uint32_t lsb; uint32_t msb; }; 

I would do it in double. Is it safe to write directly:

 pts_t t; double timestamp = t; 

and more complicated, if the struct type is part of the C dll API without the "packing" attribute (for all compilers), in this case I need to copy pts_t* to receive the instance I create through pts_t* in the API to control the packaging of the structure?

 void f(pts_t* t) { pts_t myt; myt.lsb = t->lsb; myt.msb = t->msb; double timestamp = *(double*)(&myt.lsb); } 
+6
source share
4 answers

The initial thought would be as follows:

 double timestamp = *( ( double * ) &( t.lsb ) ); 

To accomplish this (if you are in a 32-bit environment):

  • You get the identifier address t.lsb because you need to find the memory address of the first byte in your structure. Please note that you can also do &t .
  • Then you select this memory address as a double pointer (8 bytes).
  • You finally play this pointer and store all 8 bytes in an 8-byte memory block that uses the timestamp identifier.

Note:

  • You will need to consider a small / large consistency.
  • You assume that both the structure and the double are aligned correctly (as indicated below).
  • This is not portable, you think double has 8 bytes.

Now you can worry a lot about the three points in the commercial. This becomes a big pain when porting this code to multiple platforms. As mentioned below, using C unions is a much better and correct solution that is portable.

This would be written as follows: C unions :

 double timestamp = ( union { double d; struct pts_t pts; } ) { t } .d; 
+3
source

Even if you assume that your double is 64 bits wide, if you are looking for portable code, you have to be very careful with this: your structure and double may have different alignment restrictions, and your compiler may get confused because of alias rules. The way to avoid problems with this is to use union

 union { struct pts_t pts; double timestamp; } x = { .pts = t }; 

and then use x.timestamp .

Also be careful not to double compose, as this could lead to strange values, such as infinities, which you would not encounter otherwise.

+4
source

You need to write

 double timestamp = *( (double*)(&t.lsb)); 

You should use something like

 struct __attribute__((__packed__)) pts_t { ... }; 

to make sure your structure is packed (although I can’t understand why any compiler will write something after lsb in this case).

...

In fact, whether your platform depends on large or small, you may need to switch lsb and msb or do something like this:

 double timestamp; double* p_timestamp = &timestamp; *((uint32_t*)p_timestamp) = t.msb; *( ((uint32_t*)p_timestamp) + 1) = t.lsb; 
+3
source

You have:

 struct pts_t { uint32_t lsb; uint32_t msb; }; 

It:

 pts_t t; double timestamp = t; 

completely "safe", in the sense that it will not compile, so it cannot harm. You have not defined type pts_t ; you defined type struct pts_t .

It:

 struct pts_t t; double timestamp = t; 

also will not compile, because you cannot convert (either explicitly, cast, or implicitly with an assignment) a value of type struct for an object with a numeric type.

I humbly suggest that you save some time if you tried this before publication.

Probably the easiest approach is to use memcpy() :

 #include <assert.h> #include <string.h> /* ... */ struct pts_t t = { some_lsb_value, some_msbZ_value }; double timestamp; assert(sizeof t == sizeof timestamp); memcpy(&timestamp, &t, sizeof timestamp); 

By using memcpy() rather than casting pointers, you avoid the risk of improper memory access; depending on the system, your structure may require more or less strict alignment than double .

There is also no guarantee that your structure is the same size as double . The structure is almost certainly 64 bits, and double is probably 64 bits, but none of them are guaranteed, and it does not interfere with making your assumptions explicit.

This leaves open the question of whether the values ​​that you saved in t.lsb and t.msb , taken together, represent a valid double value, especially for the desired value. The language says very little about how floating point types are represented. In particular, consensus can and does depend on different systems. It is up to you to make sure that reinterpreting the view in this way makes sense - and your code is most likely not portable.

0
source

Source: https://habr.com/ru/post/951376/


All Articles