Проблемы с преобразованием из числа с плавающей точкой от младшего к старшему и обратно - PullRequest
0 голосов
/ 22 апреля 2019

Я пытаюсь написать синтаксический анализатор сохранения, который сохраняет числа с плавающей запятой с прямым порядком байтов, однако Java находится в старшем порядке, поэтому мне нужно преобразовать FP и обратно при записи, но это вызывает некоторые несоответствия в некоторых плавающихчисла точек.

Я попытался прочитать число с плавающей запятой, преобразовать в биты int, обратить биты int и затем преобразовать обратно в число с плавающей запятой.

и наоборот преобразовать число с плавающей точкой в ​​необработанное intбиты, обращая биты int и затем возвращая обратно к плавающей точке.


    public void test()
    {
        //file contents (hex) 0x85, 0x76, 0x7e, 0xbd, 0x7f, 0xd8, 0xa8, 0x3e, 0x2f, 0xcb, 0x8f, 0x3d, 0x06, 0x7c, 0x70, 0x3f
        RandomAccessFile iRAF = new RandomAccessFile(new File("input.test"), "r");
        RandomAccessFile oRAF = new RandomAccessFile(new File("output.test"), "rw");

        byte[] input = new byte[16];
        iRAF.readFully(input);

        float[] floats = new float[4];
        for (int i = 0; i < 4; i++)
        {
            floats[i] = readFloat(iRAF);
        }
        writeFloats(oRAF, floats);

        byte[] output = new byte[16];
        oRAF.seek(0);
        oRAF.readFully(output);

        if (Arrays.equals(input, output) == false)
        {
            System.err.println(toHex(input));
            System.err.println(toHex(output));
        }
    }

    private String toHex(byte[] bytes)
    {
        StringBuilder sb = new StringBuilder(bytes.length * 2);
        for (int i = 0; i < bytes.length; i++)
        {
            sb.append(String.format("%02x", bytes[i])).append(" ");
        }
        return sb.toString();
    }

    public float readFloat(RandomAccessFile raf) throws IOException
    {
        return Float.intBitsToFloat(Integer.reverseBytes(Float.floatToRawIntBits(raf.readFloat())));
    }

    public void writeFloats(RandomAccessFile raf, float... floats) throws IOException
    {
        for (int i = 0; i < floats.length; i++)
            raf.writeFloat(Float.intBitsToFloat(Integer.reverseBytes(Float.floatToRawIntBits(floats[i]))));
    }

Я ожидаю, что выход будет иметь точно такое же шестнадцатеричное значение, что и вход:

85 76 7e bd 7f d8 a8 3e 2f cb 8f 3d 06 7c 70 3f

, но фактический выход составляет:

85 76 7e bd 7f c0 00 00 2fcb 8f 3d 06 7c 70 3f

это из-за некоторой ошибки округления с плавающей запятой, или, возможно, при преобразовании это преобразование в значение NaN и не сохранение битов (хотя я бы подумал, что это то, чтоt Float.floatToRawIntBits () предназначен для.

1 Ответ

3 голосов
/ 23 апреля 2019

Я полагаю, вы столкнулись с коллапсом NaN.Если вам действительно нужно различать различные значения NaN, у вас будет больше проблем, чем просто хранилище файлов.Согласно Спецификации языка Java, 4.2.3.Типы, форматы и значения с плавающей точкой :

IEEE 754 допускает множественные различные значения NaN для каждого из его одинарных и двойных форматов с плавающей точкой.В то время как каждая аппаратная архитектура возвращает определенный битовый шаблон для NaN, когда генерируется новый NaN, программист может также создавать NaN с различными битовыми шаблонами для кодирования, например, ретроспективной диагностической информации.

По большей части,Java SE Platform обрабатывает значения NaN данного типа, как будто они свернуты в одно каноническое значение, и, следовательно, эта спецификация обычно ссылается на произвольный NaN как на каноническое значение.

Я спросил: «Почемувы используете Float.floatToRawIntBits (raf.readFloat ()) вместо raf.readInt ()? "потому что я пытался понять и, возможно, упростить вашу тестовую программу, не ожидая решения проблемы.

...