Ошибка точности от Little Endian до Big Endian

Я пишу программу на Java для ввода файла, который будет использоваться другой программой, написанной на C ++. Как следствие, этот файл должен быть двоичным файлом чисел с обратным порядком байтов с обратным порядком байтов.

Я использовал много разных способов сделать это преобразование порядка байтов, включая Apache Commons I / O, утилиту Geosoft (которая является тем же самым, что и apache ..) и ByteBuffer.

Таким образом, я должен взять кучу чисел с плавающей запятой и отобразить их в двоичном порядке с прямым порядком байтов. Этот процесс работает большую часть времени. Однако в некоторых случаях кажется, что есть некоторая ошибка точности в преобразовании порядка байтов, когда число с плавающей точкой распознается как NaN. В результате вместо определенного числа с плавающей запятой отображается число вроде 6.055E-41 или что-то в этом роде, когда я читаю только что записанный двоичный файл примерно в 1% случаев. Я искал похожие проблемы в Интернете.

Еще один человек диагностировал ошибку, содержащуюся в преобразовании между числами с плавающей запятой в биты Int и битами с плавающей запятой (обнаружено в утилитах Apache и Geosoft).

Рекомендуемое решение заключалось в том, чтобы преобразовать файл непосредственно в биты типа int, а не выполнять этот раундовый процесс. Однако преобразование непосредственно в целые числа в моем случае заставит меня потерять точность исходных чисел с плавающей запятой, которые мне нужно записать. Рекомендуемое решение для вашей справки:

"Путем чтения числа с плавающей запятой с помощью процедуры readFloat () DataInputStream, преобразования его в биты с помощью Float.floatToIntBits (), замены целого числа и последующего преобразования его обратно в число с плавающей запятой с помощью Float.intBitsToFloat () в некоторых случаях приводит к ошибке точности, что приводит к возвращение барахла.

Решение состоит в том, чтобы написать новый набор процедур, которые считывают биты из потока байтов непосредственно в целые числа, а затем выполняют обмен байтов и преобразовывают их обратно в число с плавающей запятой ".

Вот пример проблемы ...

При записи этих серий чисел с плавающей запятой в двоичный файл и чтении их обратно ожидаемый результат будет следующим:

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

}

Постановили: Благодаря Луи Вассерману ответ на мои проблемы был найден:

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

person newproduct    schedule 16.07.2013    source источник
comment
Float.floatToIntBits совершенно не требует точности. Почему вы предполагаете, что это будет? (Я имею в виду, что он объединит разные NaN, но это не должно быть проблемой для вас, и даже если бы это было так, вы могли бы вместо этого использовать Float.floatToRawIntBits.)   -  person Louis Wasserman    schedule 16.07.2013
comment
@Louis Я уже пробовал Float.floatToRawIntBits, и это не имеет значения. Что касается первой части комментария, то, думаю, это не какая-то ошибка точности. Но какое бы имя ни было присвоено для классификации ошибки, число с плавающей запятой распознается как последовательность NaN. Я не знаю, как это исправить. Я хотел сказать, что преобразование напрямую в Int вместо шага обходного преобразования определенно должно стоить точности.   -  person newproduct    schedule 16.07.2013
comment
float - это 32 бита. int - это 32 бита. Float.floatToRawIntBits и Float.intBitsToFloat - идеальные противоположности; это должно быть полностью обратимым. У вас есть тест, который более конкретно демонстрирует проблему?   -  person Louis Wasserman    schedule 16.07.2013
comment
Извините, я не понимаю. Я согласен с тем, что использование обратных процедур ничего не меняет. Проблема в том, что некоторые числа с плавающей запятой в этом процессе распознаются как NaN. Что касается точности, я имел в виду предложенное решение аналогичной проблемы на другом форуме. Я добавляю предложение человека к самому вопросу вместе с несколькими тестовыми примерами.   -  person newproduct    schedule 16.07.2013
comment
@Louis Пример проблемы возник. Это происходит только в 1% случаев.   -  person newproduct    schedule 16.07.2013
comment
@Louis Кроме того, аномалия проходит тест FLOAT.isNan (), поэтому она подтверждается как NaN. Как мне предотвратить активацию этой последовательности распознавания NaN при преобразовании порядка байтов числа с плавающей запятой?   -  person newproduct    schedule 16.07.2013
comment
@LouisWasserman Я снова попробовал RawIntBits. Мне было интересно, правильно ли я это делаю ... Я все еще не могу извлечь значения NaN. Вот что у меня есть: публичный статический обмен с плавающей запятой (значение с плавающей запятой) {int intValue = Float.floatToRawIntBits (значение); intValue = своп (intValue); вернуть Float.intBitsToFloat (intValue); }   -  person newproduct    schedule 19.07.2013
comment
Почему вы звоните intValue = swap(intValue)?   -  person Louis Wasserman    schedule 19.07.2013
comment
Я назвал его, так как это строка, которая меняет местами байты.   -  person newproduct    schedule 19.07.2013
comment
Это другой swap метод? Вы определяете float swap(float value), а затем вызываете другой метод swap? И имеет ли смысл возвращать значение с обратным порядком байтов к float, а не сохранять его как int?   -  person Louis Wasserman    schedule 19.07.2013
comment
Да, это другой метод подкачки, который принимает int. Код выложу в вопросе. Программа на C ++, в которую я ввожу двоичный файл, требует числа с плавающей запятой, поэтому я подумал, что необходимо выполнить возврат. Эти методы аналогичны тем, которые можно найти в файле Apache Commons I / O EndianUtils.java.   -  person newproduct    schedule 19.07.2013
comment
Если вы запишете их как ints, это, вероятно, все равно должно работать, если программа C ++ пытается прочитать их как байты. FWIW, базовый JDK имеет метод для изменения порядка байтов int: Integer.reverseBytes(int).   -  person Louis Wasserman    schedule 19.07.2013
comment
Это глупый вопрос, но я не совсем уверен, как это сделать. Я попытался использовать новый метод подкачки, который я сделал (код вставлен в вопрос), и продолжаю получать исключение numberFormatException. Я серьезно надеюсь, что не делаю глупых ошибок .. Вот раздел журнала: Исключение в основном потоке java.lang.NumberFormatException: Для входной строки: 10111111001000110110010110001110 в java.lang.NumberFormatException.forInputString (NumberFormatException.java:48)   -  person newproduct    schedule 19.07.2013
comment
Вы получаете числа из входного потока или записываете их в выходной поток? все еще непонятно, что вы пытаетесь сделать. Если вы читаете их из InputStream, вам нужно просто написать одну строку Float.intBitsToFloat(Integer.reverseBytes(dataInputStream.readInt())). Если вы пишете их в OutputStream, вы должны просто написать одну строку dataOutputStream.writeInt(Integer.reverseBytes(Float.floatToRawIntBits(float))). Все остальное просто ходит по кругу.   -  person Louis Wasserman    schedule 19.07.2013
comment
Я делаю последнее. Спасибо за вашу помощь. Я попробую.   -  person newproduct    schedule 19.07.2013
comment
@LouisWasserman Вы были правы. Это сработало. Спасибо!   -  person newproduct    schedule 22.07.2013
comment
Можете ли вы опубликовать официальный ответ или что-то в этом роде, если хотите, чтобы я мог подтвердить это? Я не совсем уверен, как с этим справиться, так как я новичок на сайте.   -  person newproduct    schedule 22.07.2013


Ответы (1)


Вы получаете числа из входного потока или записываете их в выходной поток? До сих пор непонятно, что вы пытаетесь сделать. Если вы читаете их из InputStream, вам нужно просто написать одну строку Float.intBitsToFloat(Integer.reverseBytes(dataInputStream.readInt())). Если вы пишете их в OutputStream, вам нужно просто написать одну строку dataOutputStream.writeInt(Integer.reverseBytes(Float.floatToRawIntBits(float)))‌. Все остальное просто ходит по кругу.

person Louis Wasserman    schedule 22.07.2013