QDataStream иногда использует 32-битные, а иногда и 40-битные числа с плавающей запятой.

Я пишу приложение, которое должно записывать массив поплавков в файл WAVE. Я использую для этого QDataStream, но это приводит к очень маловероятному результату, который я не могу объяснить. Кажется, что QDataStream иногда выбирает 32-битные числа с плавающей запятой, а иногда 40-битные числа с плавающей запятой. Это портит весь выходной файл, поскольку он должен подчиняться строгому формату.

Мой код примерно выглядит так:

float* array;
unsigned int nSamples;

void saveWAV(const QString& fileName) const 
{
    QFile outFile(fileName);
    if (outFile.open(QIODevice::WriteOnly | QIODevice::Text)) 
    {
        QDataStream dataStream(&outFile);
        dataStream.setByteOrder(QDataStream::LittleEndian);
        dataStream.setFloatingPointPrecision(QDataStream::SinglePrecision);

        // ... do all the WAV file header stuff ...

        for(int ii = 0; ii < nSamples; ++ii) 
            dataStream << array[ii];
    }
}

Я не могу придумать причину, по которой этот код мог иметь такой побочный эффект. Поэтому я сделал минимальный пример, чтобы выяснить, что происходит. Я заменил цикл for на это:

float temp1 = 1.63006e-33f;
float temp2 = 1.55949e-32f;

dataStream << temp1;
dataStream << temp1;
dataStream << temp2;
dataStream << temp1;
dataStream << temp2;

Затем я открыл выходной файл с помощью Matlab и посмотрел на байты, записанные в файл. Это были:

8b 6b 07 09      // this is indeed 1.63006e-33f (notice it's Little Endian)
8b 6b 07 09 
5b f2 a1 0d 0a    // I don't know what this is but it's a byte to long
8b 6b 07 09 
5b f2 a1 0d 0a

Я выбрал значения довольно произвольно, они просто имели такой эффект. Некоторые значения экспортируются как 4-байтовые, а другие как 5-байтовые числа. Кто-нибудь знает, что может быть причиной этого?

Изменить: при проверке размера обоих чисел с плавающей запятой оказалось, что они имеют длину 4 chars:

    qDebug() << sizeof(temp1); // prints '4'
    qDebug() << sizeof(temp2); // prints '4'

person Yellow    schedule 29.03.2013    source источник


Ответы (3)


Ответ заключается в открытии выходного файла: флаг QIODevice::Text должен был быть опущен, так как это двоичный файл. Если текстовый флаг включен, перед каждым символом 0a вставляется символ 0d. Таким образом, каждое число с плавающей запятой, содержащее 0a символа, из-за этого кажется на char длиннее.

Все кредиты для этого ответа относятся к ответам, данным в: Длина числа с плавающей запятой изменяется между 32 и 40 битами

person Yellow    schedule 02.04.2013

Примечание. Я не уверен на 100%, что я прав ниже, и хотел бы услышать, что я ошибаюсь, но я думаю, что это так:

QDataStream имеет собственный формат сериализации, и пока я не проверял, что наверное связано с этим. Дело в том, что он не предназначен для того, что вы пытаетесь с ним сделать: написать любой двоичный формат. Вы можете использовать этот класс, но я считаю, что вам нужно использовать только writeRawData() и позаботьтесь о порядке байтов и т. д. самостоятельно.

person hyde    schedule 29.03.2013
comment
Спасибо, это, по крайней мере, дает возможность сделать то же самое без цикла for; Замечательно. Но это по-прежнему вызывает ту же несогласованность, некоторые поплавки длиннее других! Кажется, это означает, что числа с плавающей запятой в массиве имеют переменную длину (4 или 5 байтов), что кажется маловероятным. - person Yellow; 29.03.2013

У меня была похожая проблема, хотя я не использовал флаг IODevice::Text. Я обнаружил, что добавление строки

dataStream.device()->setTextModeEnabled(false);

решил проблему, чтобы убедиться, что вы находитесь в двоичном режиме.

person Fiesta    schedule 22.04.2013