Search code examples
ctype-conversionuint32

Convert uint32 floating point representation to uint8


Without going into too many details, I have two embedded systems, neither of which can use the floating point library. In between them is a mobile application, where some calculations are performed. One calculation needs to keep the precision. This value is sent to the client via Bluetooth, as a byte array. When it's received I am then storing it as a uint32. This value is then shared again with the mobile application, where the precision is needed. There is no problem there because I am able to use Java's ByteBuffer class. The problem is I also need to share this value with a Z80 microprocessor as an uint8 because it is transmitted over a UART, and is used as an ADC count (0-255), so it loses the precision (but it's not needed on this end).

So I am doing this in my mobile app using Java:

int bits = Float.floatToIntBits(fAdcCount);
byte[] b = new byte[4];
b[0] = (byte)(bits & 0xff);
b[1] = (byte)((bits >> 8) & 0xff);
b[2] = (byte)((bits >> 16) & 0xff);
b[3] = (byte)((bits >> 24) & 0xff);

b is then being sent in a BLE characteristic write to the BLE microcontroller. The BLE microcontroller then reads this buffer as a little-endian 32-bit word and stores it in a uint32. Looking at this uint32 in the debugger shows the correct values, and like I said above I am able to put this uint32 back into a byte array and send it to the mobile app and read it using Java's ByteBuffer class. This works fine, I get the correct floating point value.

The problem is I need the integer part of this floating point representation to send to a Z80 microprocessor over UART as a uint8 because it is used as an ADC count from 0-255.

So, how can I convert a floating point value that's been wrapped up into an uint32 (little endian byte order) to an uint8 losing the precision? I also know that the range is from 0-255, meaning there will never be anything greater than 255.0 to convert.

For example if I have this:

uint32 fValue = 0x43246ADD; // = 164.417...

How can I get this without using float?:

uint8 result = 164;


Solution

  • Some lightly tested code to get OP started. Uses lots of constants to allow customization and various degrees of error checking. OP has not addressed rounding: round to nearest, toward 0, or ??. Following truncates toward 0.

    #include <stdint.h>
    #define MANTISSA_BIT_WIDTH 23
    #define BIASED_EXPO_MAX 255
    #define EXPO_BIAS 127
    #define SIGN_MASK 0x80000000
    
    unsigned DN_float_to_uint8(uint32_t x) {
      if (x & SIGN_MASK) return 0; // negative
      int expo = (int) (x >> MANTISSA_BIT_WIDTH);
      if (expo == 0) return 0;  // sub-normal
      if (expo == BIASED_EXPO_MAX) return 255;  // Infinity, NaN
      expo -= EXPO_BIAS;
      if (expo > 7) return 255; // too big
      if (expo < 0) return 0; // wee number
      uint32_t mask = ((uint32_t)1 << MANTISSA_BIT_WIDTH) - 1;
      uint32_t mantissa = mask & x;
      mantissa |= mask + 1;
      mantissa >>= (MANTISSA_BIT_WIDTH - expo);
      return mantissa;
    }
    
    #include <stdio.h>
    int main() {
      printf("%u\n", DN_float_to_uint8(0x43246a00)); // 164
      printf("%u\n", DN_float_to_uint8(0x437e0000)); // 254
      printf("%u\n", DN_float_to_uint8(0x437f0000)); // 255
      printf("%u\n", DN_float_to_uint8(0x43800000)); // 256
      printf("%u\n", DN_float_to_uint8(0x3f7fffff)); // 0.99999994
      printf("%u\n", DN_float_to_uint8(0x3f800000)); // 1
      printf("%u\n", DN_float_to_uint8(0x40000000)); // 2
      return 0;
    }
    

    Output

    164
    254
    255
    255
    0
    1
    2
    

    Useful IEEE 754 Converter


    To round positive values to nearest (many ways to cope), ties away from zero, just look at the last bit to be shifted out.

      // mantissa >>= (MANTISSA_BIT_WIDTH - expo);
      // return mantissa;
    
      // shift as needed expect for 1 bit
      mantissa >>= (MANTISSA_BIT_WIDTH - expo - 1);
      // now shift last bit
      mantissa = (mantissa + 1) >> 1;
      // Handle special case
      if (mantissa >= 256) mantissa = 255;
      return mantissa;