有效地将 16 位 short 转换为 8 位 char

Posted

技术标签:

【中文标题】有效地将 16 位 short 转换为 8 位 char【英文标题】:Efficiently converting 16-bits short to 8-bits char 【发布时间】:2013-02-03 16:46:36 【问题描述】:

我正在研究一个没有硬件划分的 Cortex M0 cpu,所以每次我划分一些东西时,都会使用 GCC 库函数。现在我最常做的除法之一是除以 256,将短裤转换为字节。有没有什么方法可以比默认的 GCC 库更有效地做到这一点(例如通过位移)?

【问题讨论】:

运算符 '>>' 会为您解决问题吗?你知道'>> 8'吗? 你不是说除以256吗?不是 255? 您可能会对 Hacker's 的乐趣一书的第 10 章“整数除以常数”感兴趣。但是 GCC 的实现者可能已经阅读了它。在假设有更好的方法之前,您是否查看了程序集? (注意:此评论假定您确实是指 255) 如果你除以 255 得到高位 8 位,我有一个坏消息要告诉你。 @JanDvorak 不,它必须是 256,你是对的。 【参考方案1】:

根据您的 cmets,您希望 -32768 映射到 0,而 32767 映射到 255。因此,您想要的公式是:

short s = /* input value */;
unsigned char c = (unsigned char) ((s + 32768) / 256);

其他评论者指出,您可以通过右移或其他各种策略来除以 256,这是正确的 - 一个合理的版本是:

unsigned char c = (unsigned char) ((s + 32768) >> 8);

但是,没有必要进行此类优化。 GCC 非常聪明地将除以常数操作转换为特殊情况的实现,在这种情况下,它会将这两者编译成完全相同的代码(使用 -O2 -mcpu=cortex-m0 -mthumb 和 GCC 4.7.2 进行测试):

    mov     r3, #128
    lsl     r3, r3, #8
    add     r0, r0, r3
    lsr     r0, r0, #8
    uxtb    r0, r0

如果您尝试太聪明(就像其他答案中的联合或指针转换示例一样),您可能会混淆它并得到更糟糕的结果 - 特别是因为那些通过内存负载工作,并且添加 32768 意味着你已经在寄存器中有值了。

【讨论】:

你会感到惊讶...我曾经看到一个 GCC 端口,它通过发出乘法操作码来处理 C 代码中的显式移位操作 - 有人可能假设了一个硬件乘法器,因此成本相当,尽管它试图运行的实际实验硬件没有实现乘法指令。幸运的是,作为 FPGA,它很容易添加。希望分裂的情况得到更好的处理。 @ChrisStratton:是的——这就是为什么经常阅读生成的程序集很有用,只是为了确保您认为正在发生的事情实际上正在发生!尽管您描述的案例很明显是 GCC 中的一个错误。【参考方案2】:

只需投射一个指针。

unsigned char *bytes = (unsigned char*)&yourvalue;

现在,bytes[0] 将保存一个字节的值,bytes[1] 将保存另一个。 顺序取决于系统的字节顺序

【讨论】:

@JanDvorak 是的,但答案声明结果取决于字节序。所以这绝不是不正确的。此外,OP 确切地知道他正在为哪种架构开发,所以他事先知道字节顺序。 当独立于平台的代码更干净并且可能更快时,编写平台相关代码的理由几乎没有 - 程序不一定将其整个生命周期都花在最初编写的目标上。在移位情况下,优化编译器将变量保存在寄存器(而不是内存)中的可能性很高;在指针的情况下,可能有一些优化编译器足够聪明,可以确定这是您使用指针并使用寄存器实现的唯一原因,但似乎不太可能。【参考方案3】:

你可以这样使用union

#include <stdio.h>

union Word 
    struct 
        unsigned char high;
        unsigned char low;
     byte;
    unsigned short word;
;

int main(int argc, char **argv) 

    union Word word;
    word.word = 0x1122;

    printf("L = 0x%x, H = 0x%x", word.byte.low, word.byte.high);

    return 0;

【讨论】:

使用联合与位移是否会提高性能?似乎联合会更快,因为不需要额外的操作,但也许它有其他开销? @Joshua 在汇编级别使用union 只不过是mov 指令。 这将给出错误的答案,因为它假定一个大端处理器,但所讨论的目标实际上是小端。您可以修复它,但仍然存在出错的风险,尤其是当代码被移植到其他地方时。

以上是关于有效地将 16 位 short 转换为 8 位 char的主要内容,如果未能解决你的问题,请参考以下文章

在单臂霓虹灯寄存器中有效地将 8 位数字扩展到 12 位

java数据类型及转换规则

超过16位的字符串装16进制

c语言编程将16位无符号数的高8位和低8位交换.

C++中long,short,int的具体区别是啥?

在 C/C+ 中从 16 位线性 PCM 音频转换为 32 位浮点的最佳方法?