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;
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;