Ben Biles
Ben Biles

Reputation: 1

Converting 8.24 fixed point , 0.000000000000000 to 1.000000000000000 range to uint32_t in C

I'd like to convert 8.24 fixed point numbers from within range of\

0.000000000000000 -> 1.000000000000000 to uint32_t

Do I multiply decimal places or add or bitshift ?

I am receiving the 8.24 format fixed point numbers as 4 bytes

    uint8_t meterDataRX[4];

    // read 4 bytes from DSP channel
       HAL_I2C_Master_Receive(&I2cHandle,bbboxDsp_address,meterDataRX,4,1); 

    uint32_t a;

    a = (meterDataRX[0] << 24) | (meterDataRX[1] << 16) | (meterDataRX[2] << 8) | meterDataRX[3];

But not sure this is correct to start with!

The goal is to make values between uint8_t of 0x00 to 0xFF but should I make uint32_t values from 4 bytes 1st? the cast

    uint8_t b;
    b = (uint8_t)a;

Upvotes: 0

Views: 1120

Answers (3)

Ian Abbott
Ian Abbott

Reputation: 17503

You need to read the 4 byte, 8.24 fixed byte number as a 32-bit number. For a real number in the range 0 to 1 inclusive, the '8.24' fixed point number will be represented as a 32-bit number in the range 0 to 0x01000000 (integer part is 1, fractional part is 0). You wish to scale this to a number the range 0 to 0xFF.

  1. Optional step: clamp out-of-range input number to a maximum value of 0x01000000:

    if (a > 0x01000000) a = 0x01000000;

  2. Multiply by 0xFF to give a number in the range 0x00000000 to 0xFF000000:

    a *= 0xFF;

  3. Optional step: for rounding rather than truncating, add the '8.24' fixed point representation of the real value 0.5, which is 0x00800000:

    a += 0x00800000;

  4. Shift right by 24 bits to strip the fractional part:

    a >>= 24;

You will be left with a number in the range 0 to 0xFF.

Note that if you skip step 1 (clamping out-of-range numbers), inputs greater than 0x01008080 (representing the real value 1.00196075439453125) will result in arithmetic overflow. If you skip both steps 1 (clamping) and 3 (rounding), inputs greater than 0x01010101 (representing the real value 1.003921568393707275390625) will result in arithmetic overflow.

Upvotes: 1

Brendan
Brendan

Reputation: 37222

For 8.24 fixed point, each uint32_t (or equivelent) would hold a number in the range from 00000000.000000000000000000000000b to 11111111.111111111111111111111111b.

For a floating point number in the range 0.000000000000000 to 1.000000000000000, you'd have to multiply it by (1 << 24) before converting it to an unsigned integer; so that you end up with 8.24 fixed point.

For a uint8_t where 0x00 represent 0.0 and 0xFF represents 0.996; you'd have to multiply it by (1 << (24-8)) (or shift it left 16 places) to convert it to 8.24 fixed point.

For a uint8_t where 0x00 represent 0.0 and 0xFF represents 1.0; you'd have to multiply it by (1 << 24) (or shift it left 24 places) and then divide by 0xFF to convert it to 8.24 fixed point.

To convert 8.24 fixed point back to either of the cases above, you'd do the reverse (e.g. multiply by 0xFF then shift right by 24 places to get back to uint8_t where 0xFF represents 1.0).

Upvotes: 0

0___________
0___________

Reputation: 67820

if the value is between 0 and 1 you need to scale it.

but if you want to just store it in the uint32 variable you need to change the order as now (in your code) your data is big endian but stm32 uCs use little endian.

    a = ((uint32_t )meterDataRX[0] ) | ((uint32_t )meterDataRX[1] << 8) | ((uint32_t )meterDataRX[2] << 16) | ((uint32_t )meterDataRX[3] <<24);

you van also use union pinning for it

Upvotes: 0

Related Questions