C ++压缩整数的大小,低至2位?

时间:2018-03-10 21:46:11

标签: c++ integer compression encode bits

我正在做一个小型游戏物理网络项目,我正在尝试使用本指南优化我发送的数据包:

https://gafferongames.com/post/snapshot_compression/

在“优化四元数”部分中,它说:

  

由于数值精度问题,不要总是丢弃相同的组件。相反,找到具有最大绝对值的组件并使用两位[0,3](0 = x,1 = y,2 = z,3 = w)对其索引进行ENCODE,然后发送最大组件的索引和网络上最小的三个组件

现在我的问题是,我如何编码一个低至2位的整数...或者我误解了这个任务?

我对压缩数据知之甚少,但将4字节整数(32位)减少到只有2位似乎对我来说有点疯狂。这甚至可能,还是我完全误解了一切?

编辑: 这是我到目前为止的一些代码:

void HavNetConnection::sendBodyPacket(HavNetBodyPacket bp)
{
  RakNet::BitStream bsOut;
  bsOut.Write((RakNet::MessageID)ID_BODY_PACKET);

  float maxAbs = std::abs(bp.rotation(0));
  int maxIndex = 0;
  for (int i = 1; i < 4; i++)
  {
    float rotAbs = std::abs(bp.rotation(i));
    if (rotAbs > maxAbs) {
      maxAbs = rotAbs;
      maxIndex = i;
    }
  }

  bsOut.Write(bp.position(0));
  bsOut.Write(bp.position(1));
  bsOut.Write(bp.position(2));
  bsOut.Write(bp.linearVelocity(0));
  bsOut.Write(bp.linearVelocity(1));
  bsOut.Write(bp.linearVelocity(2));
  bsOut.Write(bp.rotation(0));
  bsOut.Write(bp.rotation(1));
  bsOut.Write(bp.rotation(2));
  bsOut.Write(bp.rotation(3));
  bsOut.Write(bp.bodyId.toRawInt(bp.bodyId));
  bsOut.Write(bp.stepCount);

  // Send body packets over UDP (UNRELIABLE), priority could be low.
  m_peer->Send(&bsOut, MEDIUM_PRIORITY, UNRELIABLE, 
      0, RakNet::UNASSIGNED_SYSTEM_ADDRESS, true);
}

3 个答案:

答案 0 :(得分:2)

我猜他们希望你将2位装入你已经发送的某个值,它不需要所有可用位,或者将几个小位字段打包到一个int中进行传输

你可以这样做:

int address_list;
// set address to w (11)
address_list = w;
// 0000 0011

// bit shift left by 2 bits
address_list = address_list << 2;
// 0000 1100

// now add address x (00)
address_list |= x;
// 0000 1100

// bit shift left 2 more bits
address_list = address_list << 2;
// 0011 0000

// add the address y (01)
address_list |= y;
// 0011 0001

// bit shift left 2 more bits
address_list = address_list << 2;
// 1100 0100

// add the address z. (10)
address_list |= z;
// 1100 0110
// w x  y z are now in the lower byte of 'address_list'

在接收端:

{{1}}

你可以打包&#39;以这种方式位,一次任意数量的位。

{{1}}

这将4个地址打包到&#39; address_list&#39 ;;

的低位字节

你只需要在另一端进行拆包。

这有一些实现细节可供解决。现在只有30位用于该值,而不是32.如果数据是带符号的int,则还有更多工作要做,以避免将符号位向左移,等等。

但是,从根本上说,这就是你可以将位模式填充到你发送的数据中的方式。

显然,这假设发送比将比特打包成字节和整数等工作更昂贵。通常情况下,特别是在涉及低波特率的情况下,如串行端口。

答案 1 :(得分:2)

解决您问题的最简单方法是使用bitfields

// working type (use your existing Quaternion implementation instead)
struct Quaternion{
    float w,x,y,z;
    Quaternion(float w_=1.0f, float x_=0.0f, float y_=0.0f, float z_=0.0f) : w(w_), x(x_), y(y_), z(z_) {}
};

struct PacketQuaternion
{
    enum LargestElement{
        W=0, X=1, Y=2, Z=3,
    };
    LargestElement le : 2; // 2 bits;
    signed int i1 : 9, i2 : 9, i3 : 9;  // 9 bits each

    PacketQuaternion() : le(W), i1(0), i2(0), i3(0) {}

    operator Quaternion() const { // convert packet quaternion to  regular quaternion
        const float s = 1.0f/float(1<<8); // scale int to [-1, 1]; you could also scale to [-sqrt(.5), sqrt(.5)]
        const float f1=s*i1, f2 = s*i2, f3 = s*i3;
        const float f0 = std::sqrt(1.0f - f1*f1-f2*f2-f3*f3);
        switch(le){
        case W: return Quaternion(f0, f1, f2, f3);
        case X: return Quaternion(f1, f0, f2, f3);
        case Y: return Quaternion(f1, f2, f0, f3);
        case Z: return Quaternion(f1, f2, f3, f0);
        }
        return Quaternion(); // default, can't happen
    }
};

如果您看一下这个生成的汇编程序代码,您会看到一些转移,以便将lei1提取到i3 - 基本上与您编写的代码相同手动也是。

你的PacketQuaternion结构总是会占用整个字节数,因此(在任何非常规平台上)你仍然会浪费3位(你可以在这里使用每个整数字段10位,除非你有其他用于那些位。)

我遗漏了从常规四元数转换为PacketQuaternion的代码,但这也应该相对简单。

一般情况下(与涉及网络时一样),要特别注意数据在各个方向正确转换,尤其是涉及不同的架构或不同的编译器时!

此外,正如其他人所指出的那样,确保在进行积极优化之前,网络带宽确实是瓶颈。

答案 2 :(得分:-1)

这里有很多可能的理解和误解。 ttemple解决了发送少于一个字节的技术问题。 我想重申更多的理论观点。

未完成

你最初误解了引用的段落。 我们不使用两位来说“不发送2121387”, 但要说“不发送z-component”。 这些完全匹配应该很容易看到。

这是不可能的

如果要发送一个32位整数,可能会占用2 ^ 32个可能的值, 你需要至少 32位。 由于n位最多可以表示2 ^ n个状态, 任何少量的比特都是不够的。

这有点可能

超出实际问题: 当我们放松要求我们将始终使用2位 并且有足够强大的假设 关于价值的概率分布, 我们可以得到比特数的期望值。 像这样的想法会在链接文章中的所有地方使用。

实施例

设c为几乎所有时间都为0的整数(比如97%) 并且可以在剩余的时间内取任何价值(3%)。 然后我们可以用一点来说“c是否为零” 而且大部分时间都不需要进一步的位。 在c不为零的情况下, 我们花了另外32位来定期编码。 总的来说,我们平均需要0.97 * 1 + 0.03 *(1 + 32)= 1.96位 。 但我们需要33位有时, 这使我与之前的不可能性断言兼容。

这很复杂

根据你的背景(数学,比特等),它可能看起来像一块巨大的,不可知的黑魔法。 (事实并非如此。你可以学习这些东西。) 你似乎没有完全迷失和快速学习 但我同意Remy Lebeau 你似乎超出了自己的深度。

你真的需要这样做吗? 或者你是否过早优化? 如果它运行良好,让它运行。 专注于重要的事情。