使用C中的(带符号的短)强制转换,向左和向右旋转标志扩展

使用C中的(带符号的短)强制转换,向左和向右旋转标志扩展,c,casting,rotation,unsigned,signed,C,Casting,Rotation,Unsigned,Signed,以前,我有下面的C代码,通过它,我打算在转换为变量“sample\u unsigned”的“signed short”之后对变量“sample”进行符号扩展 unsigned short sample_unsigned; signed short sample; sample = ((signed short) sample_unsigned << 4) >> 4; 我理解“样本”的结果应该是: sample = 0xF800 (corresponding to "1

以前,我有下面的C代码,通过它,我打算在转换为变量“sample\u unsigned”的“signed short”之后对变量“sample”进行符号扩展

unsigned short sample_unsigned;
signed short sample;

sample = ((signed short) sample_unsigned << 4) >> 4;
我理解“样本”的结果应该是:

sample = 0xF800 (corresponding to "1111100000000000" in binary)
然而,“sample”总是以与“sample_unsigned”相同的方式结束,我不得不将赋值语句拆分为下面的语句,这很有效。为什么会这样

sample = ((signed short) sample_unsigned << 4);
sample >>= 4;
sample=((短符号)sample\u unsigned>=4;
这是因为由于整数升级,
(短符号)sample\u unsigned
自动转换为
int
作为操作数

sample = (signed short)((signed short) sample_unsigned << 4) >> 4;
sample=(有符号短)((有符号短)sample\u unsigned>4;

也会起作用。

你的方法不会起作用。没有高兰蒂右移将保留符号。即使如此,它也只适用于16位
int
。对于>=32位
int
,你必须手动将符号复制到高位,否则它只会来回移动相同的数据。通常,sig的位移ned值非常重要-请参阅[标准](了解详细信息。某些星座调用未定义的行为。最好避免使用它们,而只使用无符号整数

但是,对于大多数平台,以下方法都有效。速度不一定较慢(在具有16位
int
的平台上,速度可能更快):

int16\t
的转换由实现定义;编译器应指定如何执行。For(几乎?)所有最近的实现都不会执行额外的操作。只需在生成的代码或编译器文档中进行验证。逻辑or依赖于使用2s补码的
intX\u t
,该补码由标准保证,而不是标准类型

在32位平台上,可能有一条内部指令用于扩展签名(例如ARM Cortex-M3/4 SBFX)。或者编译器提供一个内置函数。根据您的用例和速度要求,可能适合使用它们

更新:

另一种方法是使用位域结构:

struct {
    int16_t val : 12;    // assuming 12 bit signed value like above
} extender;

extender.val = usample;
ssample = extender.val;

这可能会导致使用与我上面建议的相同的汇编程序指令。

按位移位更改符号是未定义的行为。
带符号的short
仍将强制为
int
。右移位带符号的
也不能保证保留符号(它甚至可以是UB)。内部(带符号的short)可能不需要,只需要在执行正确的移位之前强制转换为signed即可。如前所述,这是特定于环境的,但适用于大多数两个的补码系统。无论如何,这种方式都不起作用。在移位方面,我遇到了与您和OP相同的陷阱。@rcgldr是的,这也应该起作用:
sample=(signed short)(sample_unsigned>4;
虽然结果是实现定义的,但它应该适用于大多数2的补码系统。我知道右移符号扩展会产生未定义的行为。我正在使用64位Ubuntu 12.04,在检查我的gcc文档后,我决定尝试一下。我需要执行这种操作的原因是因为我是从一个提供10位有符号值的硬件平台捕获数据,以后我需要将其保存为ASCII。我想,正如您所建议的,使用
uint16\u t
int16\u t
应该是一个更稳定的解决方案。@CarlosCastro:我已经预料到了这种应用程序。我自己也在嵌入式系统上使用它。只要不要依赖于此!上面简单的条件移动指令不会在具有条件移动指令的平台上占用更多的时钟(IIRC、x64确实提供了这样的指令,ARM确实提供了这样的指令)。请注意,这就像您问题中的代码一样,是针对12位有符号整数,而不是10位;因此您必须调整掩码!但是,如果只是为了转换,可能会有更简单的技术。如果您可以节省内存空间,您甚至可以生成一个简单的查找表。@CarlosCastro:我更新了一点答案。请随意向上投票,如果答案有用。如果您需要最佳性能,只需对变体进行基准测试(但要注意实际基准测试的内容)
uint16_t usample;
int16_t ssample;

ssample = (int16_t)usample;
if ( ssample & 0x800 )
    ssample |= ~0xFFF;
struct {
    int16_t val : 12;    // assuming 12 bit signed value like above
} extender;

extender.val = usample;
ssample = extender.val;