Types and type conversion

http://www.barrgroup.com/Embedded-Systems/How-To/C-Fixed-Width-Integers-C99 This seems like a good reference on fixed width types. This is the list they give: int8_t: signed 8-bit uint8_t: unsigned 8-bit int16_t: signed 16-bit uint16_t: unsigned 16-bit int32_t: signed 32-bit uint32_t: unsigned 32-bit int64_t: signed 64-bit uint64_t: unsigned 64-bit

This doesn't go into non-integer but that's probably enough for now.

There's also a definition that is meant to fail if variable sizes are unexpected, at least I think that's how it works? As far as I can tell this assumes that a zero-length member will force a compile error. It doesn't on MBED though so this doesn't tell us anything about the MBED compiler behavior.

static union
{
    char   int8_t_incorrect[sizeof(  int8_t) == 1];
    char  uint8_t_incorrect[sizeof( uint8_t) == 1];
    char  int16_t_incorrect[sizeof( int16_t) == 2];
    char uint16_t_incorrect[sizeof(uint16_t) == 2];
    char  int32_t_incorrect[sizeof( int32_t) == 4];
    char uint32_t_incorrect[sizeof(uint32_t) == 4];
};

Rewriting the code to generate negative numbers for fails yields the expected behavior

static union
{
    char   int8_t_incorrect[-(sizeof(  int8_t) != 1)];
    char  uint8_t_incorrect[-(sizeof( uint8_t) != 1)];
    char  int16_t_incorrect[-(sizeof( int16_t) != 2)];
    char uint16_t_incorrect[-(sizeof(uint16_t) != 2)];
    char  int32_t_incorrect[-(sizeof( int32_t) != 4)];
    char uint32_t_incorrect[-(sizeof(uint32_t) != 4)];
};

If you change the above to show an incorrect size then it will fail to compile.

One intriguing result:

uint8_t x;
static union
{
    char   mul_incorrect[-(sizeof(x*x) != 4)];
};

The result of a multiply is an int even if the variables are smaller. This sort of makes sense. The only exception is that multiplying "long long" (64 bit) values must yield a 64 bit result.

Now I don't see any point in testing the standard types, but I migrate code between PIC and ARM and can run into problems with varying integer length. This could be useful to check at compile time that my assumptions about sizes are valid, assuming the compiler does throw an error when required.

Now that leaves the high-byte, low-byte problem.

I'm still using permutations of <<8, >>8 etc.

Note to self: the doubled forms || and && are for logic e.g. if statements the single forms | and & manipulate bits.

OK so the comma is a "operator" that discards the left hand value and returns the right hand value, this seems to be most useful in macro definitions. It is also potentially a trap for the unwary as it means that a paranthesis without a function e.g. (a,b,c) is still valid, returning c.

The task of reinterpreting a float as an unsigned int without conversion seems to attract a number of solutions.

You can not do: int i=(int)f; This will not work as it causes the value of f to be truncated into an integer value but we want a bit-for-bit copy.

One form I've seen is: *((uint32_t*)&value); where &value yields a pointer, the pointer is cast to a pointer to a different type, then the pointer is dereferenced. This prevents the automatic float-integer conversion.

The shortest one I've seen is:

int i= (int &)f; Note C++ only not Standard C

I'm not certain what the cast "(int &)" means, but a declaration of int & would create a reference and a reference is like a pointer with the dereference built in so I presume it is C++ shorthand for int i= *((int *)&f);

The union method is most popular, and has the advantage of allowing easy splitting to 16 bit words e.g. for fieldbus transmission.


Please log in to post comments.