Little Endian到Big Endian精度误差

时间:2013-07-16 17:39:55

标签: java apache-commons bytebuffer endianness

我正在用Java编写一个程序来输入一个由C ++编写的其他程序使用的文件。因此,此文件必须是浮点数的小端二进制文件。

我使用了许多不同类型的方法来进行这种端点转换,包括Apache Commons I / O,Geosoft实用程序(与apache完全相同)和ByteBuffer。

总之,我必须拿一堆浮点数并以二进制小端显示它们。这个过程大部分时间都有效。但是,在某些情况下,在endian转换中似乎存在一些精度错误,其中float被识别为NaN。结果,当我读取我刚刚写过的大约1%的二进制文件时,显示的是6.055E-41之类的数字,而不是特定的浮点数。我在网上搜索过类似的问题。

另一个人已经诊断出错误包含在float到Int Bits和Int Bits之间的转换中浮动(在Apache和Geosoft Utilities中找到)。

推荐的解决方案是将文件直接转换为int位而不是围绕进程执行此操作。但是,在我的情况下,直接转换为整数会让我失去我需要编写的原始浮点数的精度。推荐的解决方案是供您参考:

  

“通过使用DataInputStream的readFloat()例程读取float,使用Float.floatToIntBits()将其转换为位,交换Integer,然后使用Float.intBitsToFloat()将其转换回float,导致某些精度错误导致垃圾返回的案件。

     

解决方案是编写一组新的例程,将字节流中的位直接读入整数,然后执行字节交换并将其转换回浮点数。“

以下是问题的一个例子......

将这些系列的浮点数写入二进制文件并将其读回时,预期的结果如下:

  

1.50411975   -1.974895   1.0301249   -0.43540177   0.8161005   0.38000694   0.43332508

但是,我看到了这一点:

  

6.9055E-41   -1.974895   1.0301249   -0.43540177   0.8161005   0.38000694   0.43332508

我正在粘贴我的代码。此版本专门使用字节缓冲区实现。你能告诉我出了什么问题以及如何解决它吗?

public int writeBinaryFile( ArrayList<P> p) throws FileNotFoundException, IOException 
{ 
    int c = 0;


    for (int i = 0; i < p(); i++)
    {
        ArrayList<Float> cube = new ArrayList<Float>();
        ArrayList<Float> dir = p.get(i).getDirection();
        ArrayList<Float> pos = p.get(i).getPosition();
        Float angle = (float) p.get(i).getAngle();
        Float id = (float) p.get(i).getPEvents().get(0).getid();
        ArrayList<Float> things = new ArrayList<Float>();
        boolean truism = true;


    if (dir.get(1) > 0 ) {  

        /*byte [] byte2 = this.float2ByteArray(id);
        float f = ByteBuffer.wrap(byte2).order(ByteOrder.LITTLE_ENDIAN ).getFloat();
        dos.writeFloat(f);*/

        for (int j = 0; j < pos.size(); j++) {
            byte [] bytes = this.float2ByteArray(pos.get(j));
            float d = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN ).getFloat();
            cube.add(d);
        }

        for (int j = 0; j < dir.size(); j++) {
            byte [] bytes = this.float2ByteArray(dir.get(j));
            float d = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN ).getFloat();
            cube.add(d);
        }

        byte [] bytes = this.float2ByteArray(angle);
        float d = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN ).getFloat();
        cube.add(d);

        if (this.checkCube(cube)) {this.writeCube(cube); c++; }

    }

    }
    return c;
}

    public byte [] float2ByteArray(float value)
{
    return ByteBuffer.allocate(4).putFloat(value).array();
}

这是我的备用交换机制:

 public static float swap (float value)
  {
    int intValue = Float.floatToRawIntBits (value);
    intValue = swap (intValue);
    return Float.intBitsToFloat (intValue);
  }

 public static int swap (int value)
  {
    int b1 = (value >>  0) & 0xff;
    int b2 = (value >>  8) & 0xff;
    int b3 = (value >> 16) & 0xff;
    int b4 = (value >> 24) & 0xff;
    return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0;
  }

我尝试使用直接使用整数的东西。这是我的特别交换器:

 public static float specialswap (float value)
 {
     int intValue = Float.floatToRawIntBits (value);
     return Integer.reverseBytes(Integer.parseInt(Integer.toBinaryString(intValue)));

}

解决:感谢Louis Wasserman,我找到了问题的答案:

dataOutputStream.writeInt(Integer.reverseBytes(Float.floatToRawIntBits(float)))‌​. 

1 个答案:

答案 0 :(得分:4)

您是从输入流中获取数字,还是将它们写入输出流?目前还不清楚你要做什么。如果您是从InputStream阅读,那么您应该只进行一行Float.intBitsToFloat(Integer.reverseBytes(dataInputStream.readInt()))。如果您将它们写入OutputStream,则应该只执行一行dataOutputStream.writeInt(Integer.reverseBytes(Float.floatToRawIntBits(float)))‌。其他一切都只是围成一圈。