2

I'm dealing with a packed binary data file that I am trying to decode, modify and recode. I need to be able to repack float values in the same way that they were unpacked. The float value in this sample code is -1865.0. What do I need to do in byte4float so that the four bytes returned are the same as I started, ie (C3 74 90 00 ).

public class HelloWorld {
    public static void main(String[] args) {
        byte[] bytes = {(byte) 0xC3,(byte) 0X74,(byte) 0X90,(byte) 0X00 };
        byte newbytes[] = new byte[4];
        float f;
        f = float4byte (bytes[0], bytes[1], bytes[2], bytes[3]);
        System.out.println("VAL Bytes : " + f);

        // Now see if we can reverse it
        // NOT Working
        newbytes = byte4float(f);
        System.out.println ("TO Bytes: "+String.format("%02X ", newbytes[0])+
                String.format("%02X ", newbytes[1])+String.format("%02X ", newbytes[2])+String.format("%02X ", newbytes[3]));

    }

    /**
     * Convert four bytes into a float value. Byte parameters
     *
     * @param a highest byte
     * @param b higher byte
     * @param c lower byte
     * @param d lowest byte
     *
     * @return float value
     */

    private static float float4byte(byte a, byte b, byte c, byte d)
    {

        int sgn, mant, exp;
        System.out.println ("IN Byte : "+String.format("%02X ", a)+
                String.format("%02X ", b)+String.format("%02X ", c)+String.format("%02X ", d));

        mant = ( b &0xFF) << 16 | (c & 0xFF ) << 8 | ( d & 0xFF);
        if (mant == 0) return 0.0f;

        sgn = -(((a & 128) >> 6) - 1);
        exp = (a & 127) - 64;

        return (float) (sgn * Math.pow(16.0, exp - 6) * mant);
    }

    /**
     * Convert float value into a four bytes. 
     *
     * @param f float value to convert
     *
     * @return byte[0] highest byte, byte[1] higher byte, byte[2] lower byte, byte[3] lowest byte

     */

    private static byte[] byte4float(float f)
    {
        byte newbytes[] = new byte[4];
        int bits = Float.floatToIntBits(f);

        newbytes[0] = (byte)(bits & 0xff);
        newbytes[1] = (byte)((bits >> 8) & 0xff);
        newbytes[2] = (byte)((bits >> 16) & 0xff);
        newbytes[3] = (byte)((bits >> 24) & 0xff);

        return newbytes;
    }

}
Andrew L
  • 243
  • 1
  • 11
  • I should also mention that float4byte was copied from a library that reads these files but does not write them. I did not work out that solution. – Andrew L Jan 02 '16 at 11:29
  • 2
    Start by understanding how an [IEEE-754 32-bit floating point value](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) is constructed. Then understand how the mantissa, exponent, and sign of your format differ. It appears from a quick look that you have a 24-bit mantissa with all bits explicit, and a 7-bit twos-complement exponent, with the high-order bit being the sign. – kdgregory Jan 02 '16 at 12:22

2 Answers2

3

The fact that your mantisse is 24 bit and exponent is 7 bits indicates that you are dealing with IBM style single precision floating points. I had some trouble figuring out why float4byte does sgn * Math.pow(16.0, exp - 6) * mant, when I realized that simply is the same as sgn * Math.pow(16, exp) * (mant / Math.pow(2, 24), which is exactly the way IBM floats work.

What you are encoding are common IEEE-754 single precision floating points. The mis-match is causing the trouble.

On the IBM floating point architecture wikipedia article you can find an example of how to encode a floating point number to IBM float bytes.

Lodewijk Bogaards
  • 19,777
  • 3
  • 28
  • 52
0

Thanks to @halfbit input, and some minor changes, this routine will convert IEEE 754 float to IBM float.

public static byte[] byte4float(float f) {
    assert !Float.isNaN(f);
    // see also JavaDoc of Float.intBitsToFloat(int)

    int bits = Float.floatToIntBits(f);
    int s = (bits >> 31) == 0 ? 1 : -1;
    int e = (bits >> 23) & 0xFF;
    int m = (e == 0) ? (bits & 0x7FFFFF) << 1 : (bits&  0x7FFFFF) | 0x800000;

    int exp = (e - 150) / 4 + 6;
    int mant;
    int mantissaShift = (e - 150) % 4;  // compensate for base 16
    if (mantissaShift >= 0) mant = m >> mantissaShift;
    else mant = m >> (Math.abs(mantissaShift));
    if (mant > 0xFFFFFFF) { mant >>= 4; exp++; }  // loose of precision */
    byte a = (byte) ((1 - s) << 6 | (exp + 64));
    return new byte[]{ a, (byte) (mant >> 16), (byte) (mant >> 8), (byte) mant };
}

I think this is right and appears to be working.

Andrew L
  • 243
  • 1
  • 11