C++ (and C for that matter) compilers are permitted to insert and append any amount of padding into a struct
as they see fit. So if your task specifies that it must be exactly 3 bytes, then this task can not be done with struct
(or class
) using just standard language elements.
Using compiler specific attributes or pragmas, you can force the compiler to not insert padding; however for bitfields the compiler still might see the need to fill up any gaps left to type alignment requirements.
For this specific task your best bet probably is to use a class like this
class CustomFloat {
protected: // or private: as per @paddy's comment
unsigned char v[3];
}
…and hoping for the compiler not to append some padding bytes.
The surefire way would be to simply to
typedef char CustomFloat[3];
and accept, that you'll not enjoy static type checking benefits whatsoever.
And then for each operation use a form of type punning to transfer the contents of v
into a (at least 32 bit wide) variable, unpack the bits from there, perform the desired operation, pack the bits and transfer back into v
. E.g. something like this:
uint32_t u = 0;
static_assert( sizeof(u) >= sizeof(v) );
memcpy((void*)&u, sizeof(v), (void const*)v);
unsigned sign = (u & SIGN_MASK) >> SIGN_SHIFT;
unsigned mant = (u & MANT_MASK) >> MANT_SHIFT;
unsigned expt = (u & EXPT_MASK) >> EXPT_SHIFT;
// perform operation
u = 0;
u |= (sign << SIGN_SHIFT) & SIGN_MASK;
u |= (mant << MANT_SHIFT) & MANT_MASK;
u |= (expt << EXPT_SHIFT) & EXPT_MASK;
memcpy((void*)v, sizeof(v), (void const*)&u);
Yes, this looks ugly. Yes, it is quite verbose. But that's what going to happen under the hood anyway, so you might just as well write it down.