Преобразование необработанных байтов в float[]

У меня есть этот код для преобразования byte[] в float[],

public float[] ConvertByteToFloat(byte[] array)
{
    float[] floatArr = new float[array.Length / sizeof(float)];
    int index = 0;
    for (int i = 0; i < floatArr.Length; i++)
    {
        floatArr[i] = BitConverter.ToSingle(array, index);
        index += sizeof(float);
    }
    return floatArr;
}

Проблема в том, что я обычно получаю NaN результат! Почему это должно быть? Я проверил, есть ли данные в byte[] и данные вроде бы в порядке. Если это помогает, пример значений:

new byte[] {
    231,
    255,
    235,
    255,
}

Но это возвращается NaN (Не число) после преобразования в число с плавающей точкой. В чем может быть проблема? Есть ли другие лучшие способы конвертации? byte[] в float[]? Я уверен, что значения, считанные в буфер, являются правильными, поскольку я сравнил их с другой моей программой (которая выполняет усиление для файла.wav).

5 ответов

Решение

Если проблема заключается в порядке байтов, вы должны проверить значение BitConverter.IsLittleEndian чтобы определить, нужно ли поменять местами байты:

public static float[] ConvertByteToFloat(byte[] array) {
  float[] floatArr = new float[array.Length / 4];
  for (int i = 0; i < floatArr.Length; i++) {
    if (BitConverter.IsLittleEndian) {
      Array.Reverse(array, i * 4, 4);
    }
    floatArr[i] = BitConverter.ToSingle(array, i * 4);
  }
  return floatArr;
}

Не проблема ли в том, что 255 в экспоненте представляет NaN (см. Википедию в разделе экспоненты), поэтому вы должны получить NaN. Попробуйте заменить последние 255 на что-то другое...

Я предполагаю, что ваш байт [] не содержит двоичное представление чисел с плавающей точкой, но либо последовательность Int8с или Int16s. Приведенные вами примеры не похожи на аудиосэмплы, основанные на числах с плавающей запятой (ни NaN, ни -2.41E+24 не находятся в диапазоне от -1 до 1. Хотя некоторые новые форматы аудио могут поддерживать операции с плавающей запятой вне этого диапазона, традиционно аудиоданные состоят из подписанных 8 или 16-битные целочисленные выборки.

Еще одна вещь, о которой вам нужно знать, это то, что часто различные каналы чередуются. Например, он может содержать первый сэмпл для левого, затем для правого канала, а затем второй сэмпл для обоих... Так что вам нужно разделить каналы при разборе.

Также возможно, но необычно, что образцы не подписаны. В этом случае вам нужно удалить смещение из функций преобразования.

Поэтому сначала вам нужно проанализировать текущую позицию в байтовом массиве в Int8/16. А затем преобразовать это целое число в число с плавающей точкой в ​​диапазоне от -1 до 1.

Если формат имеет прямой порядок байтов, вы можете использовать BitConverter. Другая возможность, которая работает с обоими порядками байтов, - это получить два байта вручную и объединить их с битовым сдвигом. Я не помню, встречается ли маленький или большой порядок байтов. Так что вам нужно попробовать это самостоятельно.

Это можно сделать с помощью следующих функций (я их не тестировал):

float Int8ToFloat(Int8 i)
{
  return ((i-Int8.MinValue)*(1f/0xFF))-0.5f;
}

float Int16ToFloat(Int16 i)
{
  return ((i-Int16.MinValue)*(1f/0xFFFF))-0.5f;
}

Если это неправильный порядок байтов (вы читаете большие порядковые числа), попробуйте это (имейте в виду, что они "небезопасны", поэтому вы должны проверить флаг unsafe в свойствах вашего проекта)

public static unsafe int ToInt32(byte[] value, int startIndex)
{
    fixed (byte* numRef = &value[startIndex])
    {
        var num = (uint)((numRef[0] << 0x18) | (numRef[1] << 0x10) | (numRef[2] << 0x8) | numRef[3]);
        return (int)num;
    }
}

public static unsafe float ToSingle(byte[] value, int startIndex)
{
    int val = ToInt32(value, startIndex);
    return *(float*)&val;
}

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

В случае, если каждый байт должен быть преобразован в число с плавающей запятой между 0 и 255:

public float[] ConvertByteToFloat(byte[] array)
{
    return array.Select(b => (float)b).ToArray();
}

Если массив байтов содержит двоичное представление чисел с плавающей запятой, существует несколько представлений, и если хранимое в вашем файле представление не соответствует стандартному представлению с плавающей запятой языка C# ( IEEE 754), произойдут странные вещи, подобные этому.

Другие вопросы по тегам