如何在C++中将两个4位整数压缩成一个8位整数?

tpgth1q7  于 2024-01-09  发布在  其他
关注(0)|答案(3)|浏览(163)

我有一个范围为[-8,7]的整数数组,我想把它们写在一个二进制文件中。由于4位足以表示这些数字,我想把每两个元素压缩在一个8位整数中,这样我就得到了一个更小的文件。这是我到目前为止所做的:

#include <iostream>
#include <stdint.h>

int main()
{
    int8_t a1{-1}, a2{1};
    uint8_t a1_4bit = (((uint8_t)a1 & 0b10000000) >> 4) + (uint8_t)a1 & 0b00000111;
    uint8_t a2_4bit = (((uint8_t)a2 & 0b10000000) >> 4) + (uint8_t)a2 & 0b00000111;
    uint8_t compact_a = (a1_4bit << 4) + a2_4bit;

    return 0;
}

字符串
我在这里尝试做的是我把符号位和三个LSB位放在一起。但是当我试图从compact_a中提取原始整数时,我得到71,而不是-11。我用于提取原始数字的代码是:

uint8_t a_re = compact_a/16 ;
    uint8_t a_im = compact_a%16 ;

    a_re = a_re << 4 ;
    a_im = a_im << 4 ;

    int8_t a_re2 = *(int8_t *) &a_re ;
    int8_t a_im2 = *(int8_t *) &a_im ;

    a_re2 = a_re2 >> 4 ;
    a_im2 = a_im2 >> 4 ;


我不确定第一个代码片段是否具有误导性或有用,因此,无论是对我的代码进行更新还是使用完全不同的方法,都值得赞赏。

iq0todco

iq0todco1#

这里的问题是运算符优先级。如果您更改

uint8_t a1_4bit = (((uint8_t)a1 & 0b10000000) >> 4) + (uint8_t)a1 & 0b00000111;
uint8_t a2_4bit = (((uint8_t)a2 & 0b10000000) >> 4) + (uint8_t)a2 & 0b00000111;

字符串

uint8_t a1_4bit = (((uint8_t)a1 & 0b10000000) >> 4) + ((uint8_t)a1 & 0b00000111);
uint8_t a2_4bit = (((uint8_t)a2 & 0b10000000) >> 4) + ((uint8_t)a2 & 0b00000111);


(note右侧部分的额外括号),它将按预期工作。
Demo
请注意,由于您正在执行按位运算,因此请使用|而不是作为一般规则使用+,以避免在以后一些项目中出现意外的符号问题。

gdx19jrr

gdx19jrr2#

掩码0b00000111似乎是错误的。你需要四位,即掩码0b1111。我不完全确定我是否理解你的方法。
基本思想是,范围[-8, 7]中的整数可以用4位二进制补码整数表示。自C++20起,有符号整数保证用二进制补码表示,这意味着:

  • 缩小较宽的有符号整数(如int)是通过对最少的4位进行位屏蔽来完成的
  • 扩展到更宽的有符号整数(如int)是通过符号扩展完成的
#include <cstdint>
#include <utility>
#include <iostream>

std::uint8_t pack(int lo, int hi) {
    return (lo & 0b1111) << 0
         | (hi & 0b1111) << 4;
}

int sx4(int x) {
    // This assumes that int is 32-bit.
    // Making this nice and portable is an exercise for the reader.
    return x << 28 >> 28;
}

std::pair<int, int> unpack(std::uint8_t data) {
    return { sx4((data >> 0) & 0b1111), sx4((data >> 4) & 0b1111) };
}

int main() {
    std::uint8_t packed = pack(-8, 7);
    auto [lo, hi] = unpack(packed);
    std::cout << lo << ' ' << hi << '\n';
}

字符串
这个打印

-8
7

请特别注意sx4函数,它将一个4位二进制补码有符号整数进行符号扩展,形成一个32位二进制补码有符号整数。sx4基本上将8Map到-8,将9Map到-7,.将15Map到-1

bjg7j2ky

bjg7j2ky3#

下面的代码展示了如何在一个字节中存储任何一对介于-8和7之间的整数。但是,在提取数字时,它确实依赖于一些实现定义的行为:具体来说,它假设编译器在对负数使用>>时进行算术右移(即保留符号)。

#include <stdint.h>
#include <stdio.h>

int main()
{
  int8_t a1 = -4;
  int8_t a2 = -5;

  // Store in compact form:
  uint8_t compact = ((a1 & 0xF) << 4) | (a2 & 0xF);

  // Extract and restore sign:
  int8_t b1 = (int8_t)compact >> 4;
  int8_t b2 = (int8_t)(compact << 4) >> 4;

  printf("a1 = %d, a2 = %d\n", a1, a2);
  printf("compact = 0x%x\n", compact);
  printf("b1 = %d, b2 = %d\n", b1, b2);
}

字符串

相关问题