C语言 将此值从11位调整为14位[关闭]

oewdyzsn  于 2023-05-28  发布在  其他
关注(0)|答案(3)|浏览(113)

已关闭,此问题需要details or clarity。目前不接受答复。
**想改善这个问题吗?**通过editing this post添加详细信息并澄清问题。

2天前关闭。
Improve this question
我有两个独立的字节的数据。在功能上,此数据组合以创建11位无符号数。我需要将其扩展到14位,然后再次将其拆分为两个字节并传输。我用这个帖子作为参考:Converting 8 bits to a scaled 12 bits equivalent
这里有一个转折:
在两种情况下都不使用最高有效位-无论是在传入数据还是传出数据中。因此,位0-6有效,位7无效。如果N=有效值,而X=无效值,则传入的数据看起来如下所示:
MSB:XXXXNNNN LSB:公司简介
传出数据需要看起来像这样:
MSB:XNNNNNNN LSB:公司简介
这是我的想法。有没有人能提出更准确的建议,或者看到我正在做的事情的问题?注:是的,LSB在设计中首先被放入发送缓冲器。

uint16_t FullVal = 0;
uint8_t LSB_Temp = 0;

LSB_Temp = ( Msg_Buff[Buff_Tail_Indx].RX_Data[2] << 1 ) | ( Msg_Buff[Buff_Tail_Indx].RX_Data[2] >> 1 );     //Shift 7-bit LSB value left then fill lsb
FullVal = ( Msg_Buff[Buff_Tail_Indx].RX_Data[1] << 8 ) | LSB_Temp;                                      //Or together bytes
FullVal = (( FullVal << 3 ) | ( FullVal >> 3 ));                                                        //Scale 11-bit to 14-bit (missing bit is LSB bit 7)
TX_Data[1] = ( FullVal & 0x007F );                                                                      //Grab LSB, shift right to scale to 7 bits
TX_Data[2] = ( FullVal >> 8 ) & 0x00FF;                                                             //Grab MSB
js81xvg6

js81xvg61#

假设我们将包括第7位伪字节的数字称为“原始”,将去除该位的数学数字称为“值”。

  • 把第7位的保存保存起来。
  • 将高于位的所有内容向下移动1位以形成11位值。
  • 通过乘以(2^14 / 2^11)= 8缩放至14位。
  • 对于包括位7的先前值的新的14位数。

就像这样:

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

#define GARBAGE_BIT (1u << 7)

int main (void)
{
  uint16_t some_11_bit = 0x765 | (1u<<7); // some raw number
  printf("11 bit raw:   %X\n", some_11_bit);

  uint16_t garbage_bit = some_11_bit & GARBAGE_BIT;

  // convert to 11 bit value
  uint16_t above_garbage = some_11_bit & 0x700u;
  uint16_t below_garbage = some_11_bit & 0x07Fu;
  some_11_bit = ((above_garbage>>1) | below_garbage);
  printf("11 bit value: %X\n", some_11_bit);

  // scale to 14 bits
  uint16_t new_14_bit = 8 * some_11_bit;
  printf("14 bit value: %X\n", new_14_bit);

  above_garbage = (new_14_bit<<1) & 0x3F00;
  below_garbage = new_14_bit & 0x07Fu;
  new_14_bit = above_garbage | garbage_bit | below_garbage;
  printf("14 bit raw:   %X\n", new_14_bit);
}

输出:

11 bit raw:   7E5
11 bit value: 3E5
14 bit value: 1F28
14 bit raw:   3EA8
jbose2ul

jbose2ul2#

另一种方法,在我看来,这是一个明显更可读的代码,是让编译器做所有的艰苦工作,通过使用C语言的位字段功能。
使用位字段和联合定义数据的结构:

#define SCALE_FACTOR  8 // 2^14/2^11
#define USABLE_BITS   7

// Assuming the CPU architecture is little endian
typedef union {
    struct {
        uint16_t low       : 7;  // LSB first
        uint16_t reserved1 : 1;
        uint16_t high      : 4;
        uint16_t reserved2 : 4;
    } bits;
    uint8_t d[2];
    uint16_t u;
} __attribute__((packed)) In_11BitData;

typedef union {
    struct {
        uint16_t low       : 7;  // LSB first
        uint16_t reserved1 : 1;
        uint16_t high      : 7;
        uint16_t reserved2 : 1;
    } bits;
    uint8_t d[2];
    uint16_t u;
} __attribute__((packed)) Out_14BitData;

然后用这个来做你的计算:

int main()
{
    // 0x17e5 = 0b0001'0111'1110'0101
    In_11BitData in = { .d = { 0xe5, 0x17 } };
   
    uint16_t value = (in.bits.high << USABLE_BITS) | in.bits.low;
    value *= SCALE_FACTOR;

    Out_14BitData out;
    out.bits.low = value;
    out.bits.high = value >> USABLE_BITS;

    printf("In:    0x%04x\n", in.u);
    printf("Value: 0x%04x\n", value);
    printf("Out:   0x%04x\n", out.u);
    return 0;
}

输出:

In:    0x17e5
Value: 0x1f28
Out:   0x3e28

Godbolt
注意:正如注解中正确指出的那样,C标准不保证位字段从LSB开始排序。所以这段代码不是严格可移植的。但事实上,这是我遇到的大多数编译器的情况。另外,如果使用C++,则可以添加验证顺序的static_assert。

gywdnpxw

gywdnpxw3#

**编辑:**虽然最初的答案通过构建组合值的所有方式,但实际上可以通过直接创建值来更有效地完成,跳过此组合值!

首先,按如下方式进行缩放:

scaled = value * targetRange / sourceRange;

在特定情况下:

scaled = value * (1 << 14) / (1 << 11)

它解决了

value << 3

由于只涉及一个移位,您可以将其独立地应用于两个字节,然后您只需得到:

uint8_t targetLow = sourceLow << 3;
// or, if it is relevant to clear out the most significant bit
// i.e. you need to guarantee a zero MSB:
uint8_t targetLow = sourceLow << 3 & 0x7f;

这只是移出最高有效位。这些,虽然需要在高字节集成!

uint8_t targetHigh = sourceHigh << 3 | sourceLow >> 4;
// or, if surplus most significant bits are not guaranteed to be zero:
uint8_t targetHigh = sourceHigh << 3 | sourceLow >> 4 & 0x07;
// ... and you additionally want to guarantee a zero MSB yourself:
uint8_t targetHigh = sourceHigh << 3 & 0x7f | sourceLow << 4 & 0x07;

已经这样了...
留下的常规部分用于创建组合值以供参考:
您的解释并不完全清楚相关位现在实际驻留在哪里,除了所有这些位至少占用各自字节中的最低有效位。自从你编辑了问题:将lowBits替换为7,将highBits替换为4 -或更好:将它们定义为 * 常量 *。
如果你想从一个字节中提取最低有效位,你可以通过简单地屏蔽掉高位来实现;为此,您需要一个掩码,其相关有效位为1,其他有效位为0;对于一般的char,您可以通过以下方式执行此操作:

unsigned char mask = ~((unsigned char)-1 << n);

或者可选地

unsigned char mask = (unsigned char)-1 << (CHAR_BIT - n);

其中,在两种情况下,n是相关最低有效位的数量。剩下的就是:

unsigned char extracted = value & mask;

如果您想删除m最低有效位,然后使用下一个n最低有效位,请执行以下操作

unsigned char extracted = value >> m & mask;

戴着和以前一样的面具
现在假设您已经对消息中的两个字节执行了此操作。您现在希望将两者结合起来。为此,您需要将提取的位转换为16位值。因此,您需要适当地转换值(或从一开始就存储在16位变量中)。假设k位位于较低字节,则只需通过以下方式合并两个值:

uint16_t combined = (uint16_t)extractedHigh << k | (uint16_t)extractedLow;

现在你有了最终的价值。然后,缩放就像往常一样发生:

scaled = value * targetRange / sourceRange;

但是,您需要确保没有溢出发生;通过将11位值缩放到14位值,您可能最终得到具有25个有效位的最大值;所以我们可以安全地使用32位来计算中间值:

uint16_t scaled = (uint32_t)combined * (1 << 14) / (1 << 11);

好吧,在这个具体的例子中,我们还可以优化一点;当然,给定情况下的商简单地是1<<32^14 / 2^11 = 2^3),乘以1<<n与直接移位n相同,所以在具体情况下,你可以简单地有:

uint16_t scaled = combined << 3; // no intermediate 32-bit necessary...

现在所有组合:

uint16_t extractedHigh = (uint8_t)byteHigh & ~((uint8_t)-1 << bitsHigh);
uint16_t extractedLow  = (uint8_t)byteLow  & ~((uint8_t)-1 << bitsLow );
uint16_t scaled = (extractedHigh << bitsLow | extractedLow) << 3;

请注意,无论输入数据是有符号的还是无符号的,都可以正确缩放-只要不关心有符号输入的缩放值中的两个最高有效位。你会 * 有符号的值,如果你例如。想把它们记录下来那么你需要符号扩展简单有效:

int16_t signedScaled = (int16_t)(scaled << 2) >> 2;

不幸的是,如果有符号值的右移是算术移位还是逻辑移位,则是实现定义的;如果您希望完全可移植,则可能需要考虑以下两种情况:

#if -1 >> 1 == -1
    int16_t signedScaled = (int16_t)(scaled << 2) >> 2;
#else
    uint16_t bit = scaled & 1 << 13;
    scaled = bit << 1 | bit << 2;
    int16_t signedScaled = scaled;
#endif

请注意,这是假设2的补码!其他格式不包括在内。
构建输出字节也很简单,我们可以从 unsigned scaled值开始:

uint8_t byteLow = scaled;
uint8_t byteHigh = scaled >> bitsLow;

这是有效的变体,不关心低字节内的最高有效位;如果这些 * 需要 * 设置为0,则需要屏蔽多余的位:

uint8_t byteLow = scaled & ~((uint8_t)-1 << bitsLow);

你不需要屏蔽高字节的位,这些都是0。

相关问题