Представляя 3 целых числа, используя один байт? - PullRequest
2 голосов
/ 14 марта 2011

У меня есть три целых числа {a, b, c}, которые, скажем, находятся в диапазоне между следующими значениями:

a - {от 1 до 120, с скачками 1}

b - {-100 до 100, с скачками 5}

c - {1 до 10, с скачками 1}

Из-за пробелаИз соображений, я хотел бы представить эти три значения, используя ТОЛЬКО 1 байт, то есть одно целое число (в диапазоне -127..128) будет представлять результаты {a, b, c}и быть сохраненным в двоичном формате на диск.

Позже, когда я прочитаю двоичные данные, я узнаю, как «проанализировать» этот 1-байт, чтобы получить значения {a, b, c}.

Есть идеи, как этого добиться?(примечание: если необходимо, для поддержки этой схемы я могу «пойти на компромисс» по диапазонам; например, например, a может быть с скачками 5. b также может быть с скачками 10 и т. д.)

Ответы [ 4 ]

5 голосов
/ 14 марта 2011

С числовой точки зрения мы имеем:

a = 120 значений, b = 41 значение, c = 10 значений

Это составляет в общей сложности 49 200 уникальных значений.Байт может представлять только 256 значений, поэтому вам нужно использовать как минимум 16 бит (два байта) для представления вашего диапазона.

Один из способов сделать это - сдвиг битов.

Например, вы можете сохранить четыре 8-битных значения в 32-битном значении и извлечь их следующим образом:

#include <iostream>
using namespace std;


int pack32(char *v)
{
    return (v[0] << 24) + (v[1] << 16) + (v[2] << 8) + v[3];
}

void unpack32(int a, char *v)
{
    v[0] = a >> 24;
    v[1] = a >> 16;
    v[2] = a >> 8;
    v[3] = a;
}

int main()
{
    char v[4] = {32, 64, 16, 8};

    cout << "Original values: ";
    for (int i = 0; i < 4 ; i++)
        cout << (int)v[i] << " ";
    cout << endl;

    int q = pack32(v);
    cout << "Packed: " << q << endl;

    unpack32(q, v);
    cout << "Unpacked: ";
    for (int i = 0; i < 4; i++)
        cout << (int)v[i] << " ";

    return 0;
}

Код, соответствующий вашим потребностям:

unsigned short pack32(unsigned a, char b, unsigned c)
{
    // Layout:
    // Bits 0 - 5 are reserved for a
    // Bits 6 - 12 are reserved for b
    // Bits 13 - 15 are reserved for c

    // Assumptions:
    // a is [2, 120] in steps of 2
    // b is [-100, 100] in steps of 5
    // c is [1, 10] in steps of 1

    // Shift a from [2, 120] to [0, 59]
    unsigned a2 = (a - 2) >> 1;
    // Shift b from [-100, 100] to [0, 40]
    unsigned b2 = b / 5 + 20;
    // Shift c from [1, 10] to [0, 9]
    unsigned c2 = c - 1;

    return a2 + (b2 << 5) + (c2 << 12);
}
2 голосов
/ 14 марта 2011

a - {от 1 до 120, с скачками 1} = 120 значений = log2 (120) = 6,9 бит

b - {-100 до 100, с скачками 5} = 41 значение = log2 (41) = 5,4 бита

c - {от 1 до 10, с скачками 1} = 10 значений = log2 (10) = 3,3 бита

Всего = 15,6 бит, так что вы можете упаковать все это в одно 16-битное значение, но не в 8-битный байт.

1 голос
/ 14 марта 2011

На основании ответа Майка, но с правильными числами:

a = 120 значений, b = 41 значение, c = 10 значений

Это составляет в общей сложности 49 200 уникальных значений.Байт может представлять только 256 значений, поэтому вам нужно использовать как минимум 16 бит (два байта) для представления вашего диапазона.

Теперь давайте предположим, что мы хотим использовать разные биты для представления каждого из этих чисел.(т.е. без сжатия, которое как-то смешивается):

a удобно помещается в 7 битах, b удобно помещается в 6 битах, а c удобно размещается в 4 битах.(Под «удобно помещается» я подразумеваю, что это наименьшее целое число битов, в которое эти данные могут поместиться.) Это 17 битов, поэтому без применения какого-либо сжатия вы могли бы также использовать отдельный байт для каждого значения.

Теперь давайте обсудим способ вписать это в один символ, изменив размеры шагов в этих значениях.

Вы можете разделить их на два 2-битных значения (по 4 значения в каждом) и одно 4-битноезначение.Или вы можете разделить их на два 3-битных значения (по 8 значений в каждом) и одно 2-битное значение.Вы можете решить, как присвоить их своим переменным a, b и c.

. Лучший способ сохранить их в C - это структура, содержащая битовые поля:

struct myvalues{
  unsigned a:3;
  signed b:3;
  unsigned c:2;
};
//look at your compiler and platform documentation 
//to make sure you can pack this properly

Затем вы можете получить доступ к полям a, b и c напрямую по имени (хотя для преобразования значений вам придётся немного посчитать).

Другие языки(Java, C # и т. Д.) Не так гибки в определении типов, поэтому вам придется прибегнуть к сдвигу битов в этих языках.

1 голос
/ 14 марта 2011

Вам понадобится довольно много компромиссов по диапазонам, чтобы собрать все в один байт.

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

  • a (3 бита)
  • b (3 бита)
  • c (2 бита)

Это даст вам 8 разных значений для a, 8 разных значений для b и 4 разных значения для c.Конечно, это на намного * на 1017 * меньше информации, чем у вас было изначально.После того, как вы выбрали такую ​​схему, все остальное зависит от:

  • Преобразования каждого исходного значения в его «сжатый» шаблон (например, для a вы можете представить 1 как 0,и 120 как 7)
  • Объединение трех сжатых значений в один байт (с использованием битового сдвига и побитового ИЛИ)
  • Позднее разделение одного байта на три сжатых значения (с использованием битового сдвигаи маскировка)
  • Преобразование каждого сжатого значения в «несжатое» значение, которое достаточно близко к исходному значению
...