How to convert IEEE-11073 16-bit SFLOAT to simple float in Java?

13.1k Views Asked by At

The title speaks for itself: How to convert IEEE-11073 16-bit SFLOAT to simple float in Java?

7

There are 7 best solutions below

3
Peter Lawrey On BEST ANSWER

You can use bit shifting. extract the sign, exponent and mantissa and shift these so they are in float format. You may need to correct for Infinity and NaN.

As @PretiP's answer points out the exponent is base 10 so you would need to multiply or divide by a power of 10 to get the final value.

3
Durandal On

I can't find any float specification associated with IEEE 11073, you probably mean a Half precision float (sometimes also called Minifloat).

The format is described sufficiently in Wikipedia to easily convert it into a normal float. Basically, you split it into the 3 fields (sign, exponent, mantissa). The sign does not need conversion, just needs to be shifted to the correct position. Then check the exponent if its MIN or MAX value, handle special cases (Inf, NaN, subnormals/denormalized). Otherwise just correct the bias of the exponent and shift to correct position. For the mantissa, add as many zeros to the right as required. Finally put everything together into an int and use Float.intBitsToFloat(bits) to convert the bits into a normal java float.

Conversion from float works almost the same, only with the additional pitfalls of rounding, overflow and underflow.

0
Petri P On

IEEE-11073 is not in public domain but you can find sufficient information in Bluetooth personal health profiles. Google up with full spec# 11073-2060. Following is copy paste from a bluetooth personal health transcoding paper:

The following information is defined in ISO/IEEE Std. 11073-2060™1-2008 [1].

The SFLOAT-Type data type is defined to represent numeric values that are not integer in type. The SFLOAT-Type is defined as a 16-bit value with 12-bit mantissa and 4-bit exponent. See Annex F.8 of [1] for a thorough definition of the SFLOAT-Type. This data type is defined as follows:

Exponent Mantissa
Size 4 bit 12 bit

16-bit float type; the integer type is a placeholder only

SFLOAT-Type ::= INT-U16

The 16–bit value contains a 4-bit exponent to base 10, followed by a 12-bit mantissa. Each is in twos-complement form.

Special values are assigned to express the following:

  • NaN [exponent 0, mantissa +(2^11 –1) → 0x07FF]
  • NRes [exponent 0, mantissa > –(2^11) → 0x0800]
  • INFINITY [exponent 0, mantissa +(2^11 –2) → 0x07FE] – INFINITY [exponent 0, mantissa –(2^11 –2) → 0x0802]
  • Reserved for future use [exponent 0, mantissa –(2^11 –1) → 0x0801]
0
epx On

This 11073 library has C code that does that:

https://github.com/signove/antidote/blob/master/src/util/bytelib.c

Should not be difficult to convert to Java.

double read_sfloat(ByteStreamReader *stream, int *error)
{
    intu16 int_data = read_intu16(stream, error);
    if (*error)
        return 0;

    intu16 mantissa = int_data & 0x0FFF;
    int8 expoent = int_data >> 12;

    if (expoent >= 0x0008) {
        expoent = -((0x000F + 1) - expoent);
    }

    float output = 0;

    if (mantissa >= FIRST_S_RESERVED_VALUE && mantissa
        <= MDER_S_NEGATIVE_INFINITY) {
        output = reserved_float_values[mantissa
                           - FIRST_S_RESERVED_VALUE];
    } else {
        if (mantissa >= 0x0800) {
            mantissa = -((0x0FFF + 1) - mantissa);
        }
        double magnitude = pow(10.0f, expoent);
        output = (mantissa * magnitude);
    }

    return output;
}

The boilerplate code:

typedef enum {
    MDER_S_POSITIVE_INFINITY = 0x07FE,
    MDER_S_NaN = 0x07FF,
    MDER_S_NRes = 0x0800,
    MDER_S_RESERVED_VALUE = 0x0801,
    MDER_S_NEGATIVE_INFINITY = 0x0802
} ReservedSFloatValues;

static const intu32 FIRST_S_RESERVED_VALUE = MDER_S_POSITIVE_INFINITY;

intu16 read_intu16(ByteStreamReader *stream, int *error)
{
    intu16 ret = 0;

    if (stream && stream->unread_bytes > 1) {
        ret = ntohs(*((uint16_t *) stream->buffer_cur));

        stream->buffer_cur += 2;
        stream->unread_bytes -= 2;
    } else {
        if (error) {
            *error = 1;
        }

        ERROR("read_intu16");
    }

    return ret;
}
1
dpjanes On

Try searching for "Personal Health Devices Transcoding_WP_V11" and it will lead you to a document from the Bluetooth Special Interest Group. In the 25 Oct 2011 / V11r00 version of the document, section 2.2 "TRANSCODING BLUETOOTH CHARACTERISTICS TO 11073 ATTRIBUTES" gives a detailed explanation and examples of how to deal with 11073-20601 FLOAT (32 bit) and SFLOAT (16 bit) numbers.

The current URL of this document is https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=242961

Note that this is likely the same document Petri P. is referencing above.

0
Jan Oelker On

Even that this post is a little bit old, I just want to post my solution, based on this java file.

public short getExponent(short value)
{
    if (value < 0)
    { // if exponent should be negative
        return (byte) (((value >> 12) & 0x0F) | 0xF0);
    }
    return (short) ((value >> 12) & 0x0F);
}

public short getMantissa(short value)
{
    if ((value & 0x0800) != 0)
    { // if mantissa should be negative
        return (short) ((value & 0x0FFF) | 0xF000);
    }
    return (short) (value & 0x0FFF);
}

public double parseSFLOATtoDouble(short value)
{
    // NaN
    if (value == 0x07FF)
    {
        return Double.NaN;
    }
    // NRes (not at this resolution)
    else if (value == 0x0800)
    {
        return Double.NaN;
    }
    // +INF
    else if (value == 0x07FE)
    {
        return Double.POSITIVE_INFINITY;
    }
    // -INF
    else if (value == 0x0802)
    {
        return Double.NEGATIVE_INFINITY;
    }
    // Reserved
    else if (value == 0x0801)
    {
        return Double.NaN;
    }
    else
    {
        return ((double) getMantissa(value)) * Math.pow(10, getExponent(value));
    }
}
0
mwojtera On

For those looking to convert SFLOAT while using Javascript, here is a neat library, that does that, and more. https://github.com/bluetoother/ble-packet

If you want to add your own characteristics just use addMeta function, defs/charMeta.js has plenty of examples!