Character encoding 用一个字节表示3个整数?

Character encoding 用一个字节表示3个整数?,character-encoding,binary-data,primitive-types,Character Encoding,Binary Data,Primitive Types,我有三个整数{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

我有三个整数{
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次等)

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

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

这使得总共有49200个唯一值。一个字节只能表示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;
}
#包括
使用名称空间std;
int pack32(字符*v)
{
返回(v[0]>16;
v[2]=a>>8;
v[3]=a;
}
int main()
{
charv[4]={32,64,16,8};

couta-{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位


Total=15.6位,因此您可以将所有这些数据打包成一个16位值,但不能打包成一个8位字节。

要将所有数据打包成一个字节,您需要在范围上做出很大让步

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

  • a(3位)
  • b(3位)
  • c(2位)
这将为
a
提供8个不同的值,为
b
提供8个不同的值,为
c
提供4个不同的值。当然,这比您最初拥有的信息要少得多。一旦您选择了这样的方案,剩下的只是:

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

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

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

一个字节只能代表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#等)在定义类型方面没有那么灵活,因此您需要在这些语言中使用位移位。

如果我在范围上妥协会怎么样?使用与上述相同的方法,并使用新的范围再次计算位数。谢谢。我需要
log2(X)
计算位数的说明。这篇文章讨论了给定所需范围的信息理论最小值。要真正实现这一点,需要使用某种压缩算法或映射表,可以适当地混合不同位数的位数。如果你想让简单的代码读和写这些值(即每个变量的位是分开的),您需要将位数四舍五入到最接近的整数,在这种情况下,此处显示的结果将是18位。@Ken:您不需要对每个组件的位数进行四舍五入,除非可能需要考虑打包/解包的效率。并且您不需要非常复杂的编码方案。例如,如果x、y和z的范围都为0..9然后你可以将其编码为100*x+10*y+z,也就是说,你不需要为每个字符使用4位,你可以用10位来完成整个过程。谢谢。如果我在范围上妥协怎么办?@Mike:你的代码向他展示了如何将信息打包成32位整数。你只需创建
struct x{unsigned char a;signed char b;unsigned char c;}
如果你要用位移位来做这个,告诉他如何把它压缩成16位,而位移位不是8的倍数。我现在脑子里有个屁。我
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