Преобразование одного в два значения UInt16 в .NET - PullRequest
1 голос
/ 30 апреля 2010

В старые добрые времена C я мог приводить float к int (предполагая 32-битную систему), выполнять некоторые битовые манипуляции (битовое и правое смещение и т. Д.) И получать верхний и нижний 16 битовые шестнадцатеричные представления числа с плавающей запятой, которые я мог бы затем сохранить в двух коротких значениях. Я не вижу простой способ сделать это в C #.

System.Convert.ToUInt16 просто выполняет преобразование с плавающей точкой в ​​int (даже после смещения вправо), что оставляет значение 0, если значение с плавающей точкой меньше 0, что не является желаемым эффектом.

//Useless leaves me with a value of 0
UIN16 s1 = (UInt16)((System.Convert.ToUInt32(d2) & 0xffff0000) >> 16);   //Capture the high word
UInt16 s2 = (UInt16)(System.Convert.ToUInt32(d2) & 0xffff);              //Capture the low word

Базовое приведение (UInt32) также не работает.

Ответы [ 4 ]

7 голосов
/ 30 апреля 2010

Я думаю, вы после класса BitConverter.

http://msdn.microsoft.com/en-us/library/system.bitconverter.aspx

        float value = 1.1f;
        byte[] bytes = BitConverter.GetBytes(value);
        short upper = BitConverter.ToInt16(bytes, 0);
        short lower = BitConverter.ToInt16(bytes, 2);
1 голос
/ 01 мая 2010

Краткий ответ: не делай этого!

Длинное объяснение: в старые добрые времена эти хаки были сделаны, потому что преобразование с плавающей точкой в ​​int (и наоборот) было на лот быстрее, чем позволяло FPU выполнять свою работу. Я могу ошибаться, но мне кажется, я помню историю успеха такой оптимизации для игры для ПК Descent (кто-нибудь помнит это? - старый добрый Quake 1 days). В этом случае это составляет 30% от разницы. По крайней мере, так я помню ..

В наше время это больше не так, фактически наоборот. Если вы компилируете свой код с устаревшим FPU x86, любое битовое кодирование должно выполняться в целочисленной единице, и каждый переход от блока с плавающей запятой к единице int должен выполняться через память. Это стоило много циклов. Обычно вы не смешиваете операции float и int одного и того же значения, и ЦП не оптимизирован для этого варианта использования.

Если, с другой стороны, код скомпилирован с набором команд SSE , вы можете воспользоваться ярлыком. Нет никаких дополнительных затрат на выполнение целочисленных операций с плавающими данными или наоборот, но прямое преобразование с плавающей точкой int <-> через SSE в первую очередь быстрее, так зачем беспокоиться?

С другими архитектурами становится намного хуже. На примерах ARM Cortex-A8 для таких примеров такие трюки будут останавливать весь ЦП минимум на 21 цикл ... Я читал страшные истории о смешивании int и float на PowerPC (загрузить / изменить / сохранить кого-нибудь?)

В итоге: не делайте этого, если не пишете для устаревшего оборудования Pentium-I. Если так, свяжитесь со мной; У меня все еще есть время и пример кода на моем жестком диске.

1 голос
/ 30 апреля 2010

Ближайшим прямым эквивалентом вашего метода C было бы сделать это с небезопасным кодом и указателями:

private static unsafe void SplitToWords(float f, out ushort s1, out ushort s2)
{
    unsafe
    {
        float* ptr = &f;
        UInt16* s2ptr = (UInt16*) ptr;
        UInt16* s1ptr = s2ptr + 1;

        s1 = *s1ptr;
        s2 = *s2ptr;
    }
}

Это позволяет вам:

public static void Main()
{
    float f = -23984.123f;
    ushort s1;
    ushort s2;
    SplitToWords(f, out s1, out s2);

    Console.WriteLine("{0} : {1}/{2}", f, s1, s2);
    Console.ReadKey();
}

Однако более распространенный управляемый способ - использовать BitConverter .

byte[] fBytes = BitConverter.GetBytes(f);
s1 = BitConverter.ToUInt16(fBytes, 2);
s2 = BitConverter.ToUInt16(fBytes, 0);

Здесь вы можете увидеть то же самое:

public static void Main()
{
    float f = -23984.123f;
    ushort s1;
    ushort s2;
    SplitToWords(f, out s1, out s2);

    Console.WriteLine("{0} : {1}/{2}", f, s1, s2);

    byte[] fBytes = BitConverter.GetBytes(f);
    s2 = BitConverter.ToUInt16(fBytes, 0);
    s1 = BitConverter.ToUInt16(fBytes, 2);
    Console.WriteLine("{0} : {1}/{2}", f, s1, s2);

    Console.ReadKey();
}
0 голосов
/ 30 апреля 2010

Существует класс BitConverter , в котором есть несколько полезных и полезных методов.

Например, метод GetBytes может преобразовывать переменные многих различных типов в байтовый массив, а метод ToUInt16 возвращает 16-разрядное целое число без знака, преобразованное из двух байтов в указанной позиции в байтовом массиве .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...