使用一个字节表示3个整数?

时间:2011-03-14 16:57:02

标签: character-encoding binary-data primitive-types

我有三个整数{abc},其范围(比如)在以下值之间:

a - {1到120,跳跃1}

b - {-100到100,跳跃为5}

c - {1到10,跳跃1}

由于空间考虑,我想仅使用1字节表示这三个值,这意味着,单个整数(在-127..128范围内)将代表{a的结果, bc}并以二进制格式存储到磁盘。

稍后,当我读取二进制数据时,我将知道如何'解析'这个1字节以获得{abc}的值。< / p>

任何想法如何实现? (注意:如果需要,为了支持这种设计,我可以在范围上“妥协”;比如说,a可以在5的跳跃中。b也可以在跳跃中10等)

4 个答案:

答案 0 :(得分:5)

从数字的角度来看,我们有:

a = 120个值,b = 41个值,c = 10个值

这使得总共有49,200个唯一值。一个字节只能表示256个值,因此您需要使用至少16位(两个字节)来表示您的范围。

这样做的一种方法是通过位移。

例如,您可以在32位值中存储四个8位值,并像这样提取它们:

#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);
}

答案 1 :(得分:2)

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位字节。

答案 2 :(得分:1)

你需要在你的范围上妥协很多,才能把所有东西都变成一个字节。

为简单起见,您可能希望将每个值存储在整数位中 - 因此要计算出每个值所需的位数。例如,您可以使用:

  • a(3位)
  • b(3位)
  • c(2位)

这将为a提供8个不同的值,为b提供8个不同的值,为c提供4个不同的值。当然,这比你原来的信息少得多 。一旦你选择了这样的计划,剩下的就是:

  • 将每个原始值转换为“压缩”模式(例如,对于a,您可以将1表示为0,将120表示为7)
  • 将三个压缩值组合成一个字节(使用位移和按位OR)
  • 稍后将单个字节拆分为三个压缩值(使用位移和屏蔽)
  • 将每个压缩值转换为合理接近原始值的“未压缩”值

答案 3 :(得分:1)

根据Mike的回答,但数字正确:

a = 120个值,b = 41个值,c = 10个值

这使得总共有49,200个唯一值。一个字节只能表示256个值,因此您需要使用至少16位(两个字节)来表示您的范围。

现在让我们假设我们想用不同的比特来表示这些数字中的每一个(即没有以某种方式混合这些数字的压缩):

a适合7位,b适合6位,c适合4位。 (通过“舒适地拟合”,我的意思是这个数据可以容纳的最小整数位数。)这是17位,所以如果不应用某种压缩,你可以为每个值使用一个单独的字节。

现在,让我们讨论一种通过改变这些值中的步长来将其拟合为一个字符的方法。

您可以将它们分成两个2位值(每个允许4个值)和一个4位值。或者您可以将它们分成两个3位值(每个允许8个值)和一个2位值。您可以决定如何将这些变量分配给变量abc

在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

然后,您可以直接按名称访问字段abc(尽管您需要进行一些数学运算来转换值。)

其他语言(Java,C#等)对于您如何定义类型并不是那么灵活,因此您需要采用这些语言中的位移。