我正在玩状态空间,这需要非常有效地存储探索状态。这意味着,我需要将多个信息存储在尽可能小的变量中。
让我们有一个非常简单的例子:想象一下,我想存储两个坐标。我可以创建一个两个整数的结构,但是每个整数都是(如果我错了,请纠正我)32b。但我的坐标都不大于 15 。零,它是16 = 2 ^ 4个不同的值,这意味着我只需要8b存储它们。因此,通过一些按位运算符魔术,我可以将这两个值存储在单个char
:
unsigned int x, y; // initialized!!!!!
char my_code = (x << 4) | y;
当然,只有当x
和y
存储在“直接代码”中时,此代码才有效(我不确定此名称。它是数字的简单二进制表示,从最大位2 ^ n到2 ^ 1)
所以我的问题是:哪些二进制代码用于存储哪些基本的C / C ++变量?
编辑:过早优化?不。我当前的任务很小,它正在为更大的问题做准备,我需要存储从0到7的4个坐标.Toose坐标是8x8上的位置。所以我需要跟踪许多独特的组合 - 因为状态空间搜索是基于生成尚未探索的新状态。
没有可能的方法来存储多个整数并使用自定义比较器函数和set
。对于像这样的大问题,我的记忆会流血,并且跟踪我已经访问过的内容也不会很好。具有可能组合大小的位集可能是最好的方法。 (你可能会说,我所描述的那个问题对于那么大的bitset而言太大了,但有一个巧妙的技巧来处理它,对这个问题很重要。)所以,我需要某种“哈希”,可以创建很多方法 - 使用模块化算术(一种答案)或按位操作。对于今天的计算机而言,这两种解决方案之间的复杂性并没有太大差别。因为我好奇,我想用更奇特的第二种方式。但为了实现这一目标,我需要知道,数字如何以二进制级别存储 - 如果有一些verid编码,那么我的想法将完全无法使用。
我的问题不是关于变量的大小 - 这些都有很好的记录。
答案 0 :(得分:4)
当然,如果
x
和y
存储在“直接代码”中,此代码才有效
我猜你正在寻找的术语是endianness。但是,无论系统的字节顺序如何,(x << 4) | y
都会为您提供相同的值。数学是与endian无关的。数学只是数学。唯一的区别在于内存布局 - 对于单个字节,即使这无关紧要。
我们可以通过一个例子来解决。假设x
为0x0A0B0C0D
而y
为0x01020304
。如果您的系统是big-endian,那意味着内存布局是:
x : 0A 0B 0C 0D
y : 01 02 03 04
x << 4 : A0 B0 C0 D0
(x << 4) | y : A1 B2 C3 D4
to char : D4
如果是小端的话:
x : 0D 0C 0B 0A
y : 04 03 02 01
x << 4 : D0 C0 B0 A0
(x << 4) | y : D4 C3 B2 A1
to char : D4
无论哪种方式,0xD4
。
虽然,您必须担心的一件事是实际转换为char
。来自[conv.integral]:
如果目标类型是无符号的,则结果值是与源一致的最小无符号整数 整数(modulo 2 n 其中n是用于表示无符号类型的位数)。 [注意:在两个人中 补码表示,这种转换是概念性的,并且位模式没有变化(如果有的话) 不是截断)。 -end note]
如果目标类型已签名,则该值如果可以在目标类型中表示,则不会更改; 否则,该值为实现定义。
如果char
未签名,则该部分定义明确。如果它已经签署,那就不是。所以我更喜欢将unsigned char
用于my_code
。
答案 1 :(得分:1)
不要试图自己破解东西。编译器完全能够直接执行此操作:
struct Packed {
unsigned x : 4;
unsigned y : 4;
};
你最好有几百万,否则拯救并不值得。
答案 2 :(得分:0)
C不要求short
,int
或long
的尺寸;它指定了它们必须能够表示的值的范围。 unsigned int
必须能够在0到65535范围内表示至少值,这意味着它必须至少 16位宽,尽管它可能(和通常是更广泛的。签名int
必须表示至少范围-32767到32767.请注意,C不强制要求两个补码表示,这就是为什么较低的值为-32767而不是-32768。
此外,除了值位之外,这些类型还可能具有填充位。我从未研究过在整数类型中使用填充位的实现。
使用常规二进制表示将积分值存储在值位中(即,N位,最左位是最高位)。
C99添加了stdint.h
标头,它定义了具有精确宽度且没有填充位的整数类型。如果您的值确实在0到15之间,那么我建议您为每个使用uint8_t
(8位,无符号)。由于这些类型适合单个字节,因此不会出现任何字节序问题。
答案 3 :(得分:0)
C / C ++标准不强制integral types的特定表示。实际上,标准允许三种不同的表示 - 两个补码,一个补码和有符号的幅度表示。幸运的是,正数在所有表示中看起来都是一样的。
无论如何,你不必关心它 - 只需避免位操作操作符,如下所示。如果坐标范围是[0,N),则可以将它们打包为更宽的数据类型:
code = N * x + y;
或
code = N * N * x + N * y + z;
对于N = 16,您可以将两个坐标打包到单个unsigned char
中:
unsigned char code = 16*x + y;