将Byte [4]转换为float - 整数[4]数组有效但字节[4]不能

时间:2016-01-01 09:48:41

标签: java floating-point

对于那些经验丰富的程序员来说,这可能是一个基本问题。我有点像菜鸟,无法解决这个问题。我正在尝试解压缩二进制文件,并且doco对浮点数的存储方式不太清楚。我找到了一个执行此操作的例程,但只有在传递字节的整数数组时它才会起作用。正确答案是-1865.0。我需要能够传递字节数组并获得正确的答案。如何更改代码以使 float4byte 返回-1865.0。提前谢谢。

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class HelloWorld {
    public static void main(String[] args) {
        byte[] bytes = {(byte) 0xC3,(byte) 0X74,(byte) 0X90,(byte) 0X00 };
        int[] ints = {(int) 0xC3,(int) 0X74,(int) 0X90,(int) 0X00 };

        // This give the wrong answer
        float f = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).getFloat();
        System.out.println("VAL ByteBuffer BI: " + f);

        // This give the wrong answer
        f = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getFloat();
        System.out.println("VAL ByteBuffer LI: " + f);

        //This gives the RIGHT answer
        f = float4int (ints[0], ints[1], ints[2], ints[3]);
        System.out.println("VAL Integer : " + f);

        // This gives the wrong answer
        f = float4byte (bytes[0], bytes[1], bytes[2], bytes[3]);
        System.out.println("VAL Bytes : " + f);

    }
    private static float float4int(int a, int b, int c, int d)
    {

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

        mant = b << 16 | c << 8 | d;
        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);
    }

    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 << 16 | c << 8 | d;
        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);
    }

}

3 个答案:

答案 0 :(得分:2)

使用ByteBuffer的解决方案不起作用的原因:字节与浮点值的(Java)内部表示不匹配。

Java表示是

System.out.println(Integer.toHexString(Float.floatToIntBits(-1865.0f)));

,提供c4e92000

答案 1 :(得分:1)

byte是用Java签名的。在计算尾数mant时,字节从byte s隐式转换为int s - 带符号&#34;扩展&#34;,即(byte)0x90(十进制) -112)转换0xFFFFFF90(32位int)。然而你想要的只是原始字节&#39; 8位(0x00000090)。

为了补偿符号扩展的影响,只需更改一行即可:

mant = (b & 0xFF) << 16 | (c & 0xFF) << 8 | (d & 0xFF)

此处,在(c & 0xFF)中,符号扩展引起的1位在(隐式)转换为int后被剥离。

编辑:

浮点数的重新打包可以通过IEEE 754表示来完成,这可以通过Float.floatToIntBits获得(这避免使用慢对数)。代码中的一些复杂性是由于从2到16的基数变化引起的:

private 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 << (mantissaShift + 4); exp--;  }
    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 };
}

该代码未考虑包装可能存在的任何规则,例如用于表示尾数的零或标准化。但它可以作为一个起点。

答案 2 :(得分:0)

感谢@halfbit以及一些测试和细微更改,此例程似乎将IEEE 754 float转换为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 };
}

我认为这是正确的,似乎正在发挥作用。