【问题标题】:fastest way to convert int8 to int7将 int8 转换为 int7 的最快方法
【发布时间】:2019-06-23 19:24:54
【问题描述】:

我有一个函数接受 int8_t val 并将其转换为 int7_t

//Bit [7] reserved
//Bits [6:0] = signed -64 to +63 offset value
// user who calls this function will use it correctly (-64 to +63)
uint8_t func_int7_t(int8_t val){
    uint8_t val_6 = val & 0b01111111;
    if (val & 0x80)
        val_6 |= 0x40;
    //...
    //do stuff...
    return val_6;
}

将 int8 操作为 int7 的最佳和最快方法是什么?我做得有效率和速度吗?还是有更好的办法?

如果重要的话,目标是 ARM Cortex M0+

更新:

阅读不同的答案后,我可以说问题被问错了吗? (或者我在问题中的代码是什么给了其他人错误的假设)我打算将 int8 变成 int7

所以什么都不做,因为

8位:

 63 = 0011 1111
 62 = 0011 1110
  0 = 0000 0000
 -1 = 1111 1111
 -2 = 1111 1110
-63 = 1100 0001
-64 = 1100 0000

7位:

 63 = 011 1111
 62 = 011 1110
  0 = 000 0000
 -1 = 111 1111
 -2 = 111 1110
-63 = 100 0001
-64 = 100 0000

【问题讨论】:

  • 你的掩码应该是0b00111111,因为除非设置了原始值符号位,否则你不希望设置“符号位”。请注意,您并没有真正转换任何内容,您只是在基本截断。
  • 在“最好和最快”之前,您需要说明正确。但是,为此,您需要解释如何处理超出“int7”可以表示的范围的输入值。
  • @JohnBollinger 哦,我删除了我认为调用此函数的用户知道他应该将数据 -64 到 +63 的评论。所以没必要去那个安全的地方..
  • @Hasanalattar 好,那么我建议你去阅读维基百科中关于 2 的补码表示。
  • “保留”没有任何意义。重要的是您要清除该位或保持原样。通常这通常意味着不要触摸它,在这种情况下你根本无事可做,只需返回整个 int8_t 值

标签: c++ c performance bit-manipulation


【解决方案1】:

更快的方法可能是:

uint8_t val_7 = (val & 0x3f) | ((val >> 1) & 0x40);

val & 0x3f 获取低 6 位(截断),((val >> 1) & 0x40) 将位从第 8 位移到第 7 位

不使用 if 的优点是代码更短(即使您可以使用算术 if)并且代码没有序列中断

【讨论】:

  • 肯定是((val >> 1) & 0x40
  • 这正是我想发布的内容...有趣的替代方案0xc0,它将保留(重复)MSB 中的符号位(提供算术右移,但应该在 ARM 上)。
  • ((val & 0x80) >> 1)。这避免了与右移负数相关的实现定义的行为。
  • @JohnBollinger 我首先右移 然后 做 '&' 以独立于它是否是有符号移位的事实,((val & 0x80) >> 1) 不一样结果是否在 8 位算术上进行有符号移位(但 ofc 全部在 int 上完成)
  • 这是最慢的方法!那么为什么它获得了所有的支持呢?
【解决方案2】:

要清除保留位,只需

return val & 0x7f;

要保留保留位与输入时完全相同,无需执行任何操作

return val;

低 7 位将包含 [-64, 63] 中的值。因为在二进制补码中向下转换是通过简单的截断来完成的。值保持不变。这就是像(int8_t)some_int_value这样的任务发生的情况@

没有0bX1100001这样的东西。机器语言中没有未定义的位。该状态仅存在于硬件中,例如 Verilog 或其他硬件描述语言中的高 Z 状态或未定义状态

【讨论】:

  • 你的意思是[-64,63]。或[-64,64).
  • @TonyK 是的,修正了错字
  • 清除保留位对于允许范围内的输入具有完全正确的语义。通过看逆变换我们可以看出这一点:如果我们有一个负数的 7 位二进制补码表示,那么符号扩展至 8 位意味着添加前导 1 位。这就是保值。因此,以另一种方式返回只需要忽略或删除额外的位。
  • func_int7_t(-63) 它应该返回 [0bX1100001]。我的问题不涉及第 8 位。 [X] = 不管是 0 还是 1!我的评论是错误的,因为它应该返回 [0bX1111110],即(-63)(7 位二进制表示)
  • 没关系,那为什么还要问这个呢?只需返回上面的值。 -63 不是 1111110。你看到最后一位是零,表示偶数吗?尝试打开计算器,你会立即看到 -63 = 1000001
【解决方案3】:

使用位域来缩小值,让编译器选择最适合您平台的移位和/或掩码序列。

inline uint8_t to7bit(int8_t x) 
{ 
    struct {uint8_t x:7;} s; 
    return s.x = x; 
} 

【讨论】:

  • Interesting... 从标准的角度来看,编译器可以选择从左到右或从右到左填充位字段(仅成员的顺序必须保持不变),所以至少在理论上,这不是完全可移植的。但是,指定的工具链从 MSB 开始填充,所以我们很好......(实际上,我不知道还有其他不这样做的工具链 - 也许在某些 BE 平台上?)
  • 我在代码中只使用了 s.x,我尝试过的所有编译器(gcc、clang、msvc)都完全优化了 s 的使用。
  • @Aconcagua,编译器如何表示位域的值并不重要。这种方法中使用的任何操作都不依赖于产生所需结果的操作。我不同意这是最好的,但它会正常工作。
  • @JohnBollinger 输出 is 在任何情况下都是正确的 - 但是,选择填充从 MSB 开始的位域的编译器可能需要(最多)两个额外的移位,并且解决方案可能不再是最快的了......可能只是理论上的兴趣,如前所述,我也不知道任何这样的编译器......
【解决方案4】:

如果您不关心超出范围的值会发生什么,那么

return val & 0x7f;

就够了。这可以正确处理-64 <= val <= 63 范围内的值。 你还没有说你想如何处理超出范围的值,所以我对此无话可说。

更新添加:问题已更新,因此规定永远不会使用超出范围的值调用该函数。因此,这种方法无疑是“最好和最快的”。

【讨论】:

  • 因此,-127 (0b10000001) 将变为 1。错误。
  • 如果初始值为 0x82(负) 0x82 & 0x7f 为 0x2 所以是正值,这样不行
  • @AndrejsCainikovs,-127 不是受支持的参数值。根据函数规范,参数必须介于 -64 和 63 之间。
  • @bruno, 0x82 也不是受支持的参数值。
  • @Hasanalattar: -631111111,而不是 1100001
【解决方案5】:

调用这个函数的用户知道他应该把数据 -64 到 +63

所以不考虑任何其他值,真正你能做的最快的事情就是什么都不做

您有一个 7 位值存储在 8 位中。指定范围内的任何值都将具有相同的第 7 位和第 6 位值,并且当您处理 7 位值时,您只需 忽略 MSB(8 位值),无论是否设置与否,例如。 g.:

for(unsigned int bit = 0x40; bit; bit >>= 1)
//                NOT: 0x80!
    std::cout << (value & bit);

反之更为关键:每当您通过某个通信渠道接收到这七个位时,您需要手动扩展八个(或更多)位的符号才能正确使用该值。

【讨论】:

  • 我同意有效输入的低 7 位已经正确,因此无需对它们进行任何操作。这实际上是两个答案的重点,这表明只是掩盖了另一个。那么,也许,什么都不做确实是最好的,但这与示例代码不一致,这迫使另一点明确。
  • @JohnBollinger 现在的问题是:清除该位是否真的是一个要求,或者在提出问题时这是一个误解......
  • 我在这个问题中的独奏是错误的,这会导致错误的答案。不,实际上我开始觉得这会很复杂。调用值为 [0b11000001] 的函数应返回 [0bX1111111] - X 表示我不在乎。
  • @Hasanalattar 为什么应该是 1111111?如果您使用一些奇怪的编码而不是二进制补码,那么您必须在问题中包含映射[
  • @Hasanalattar,在 7 位二进制补码中,0b1111111 表示 -1,而不是 -63。 -63 将表示为 0b1000001。如果您的意思是转换为符号/幅度表示,那么那是完全不同的鱼。
猜你喜欢
  • 1970-01-01
  • 2012-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-17
相关资源
最近更新 更多