简体   繁体   English

向左和向右旋转以使用C转换(带符号短号)进行符号扩展

[英]Rotate left and back to the right for sign extension with (signed short) cast in C

Previously, I had the following C code, through which I intended to do sign extension of variable 'sample' after a cast to 'signed short' of variable 'sample_unsigned'. 以前,我有以下C代码,通过该代码,我打算在将变量“ sample_unsigned”的“ signed short”转换为变量后,对变量“ sample”进行符号扩展。

unsigned short sample_unsigned;
signed short sample;

sample = ((signed short) sample_unsigned << 4) >> 4;

In binary representation, I would expect 'sample' to have its most significant bit repeated 4 times. 以二进制表示,我希望“样本”的最高有效位重复4次。 For instance, if: 例如,如果:

sample_unsigned = 0x0800 (corresponding to "100000000000" in binary)

I understand 'sample' should result being: 我了解“样本”应为:

sample = 0xF800 (corresponding to "1111100000000000" in binary)

However, 'sample' always ended being the same as 'sample_unsigned', and I had to split the assignment statement as below, which worked. 但是,'sample'总是以与'sample_unsigned'相同的方式结束,因此我不得不按如下方式拆分赋值语句,这是可行的。 Why this? 为什么这个?

sample = ((signed short) sample_unsigned << 4);
sample >>= 4;

It is because (signed short) sample_unsigned is automatically converted to int as operand due to interger promotion. 这是因为由于整数提升, (signed short) sample_unsigned整数(signed short) sample_unsigned会自动转换为int作为操作数。

sample = (signed short)((signed short) sample_unsigned << 4) >> 4;

will work as well. 也会工作。

Your approach will not work. 您的方法无效。 There is no gaurantee right shifting will preserve the sign. 没有保证的右移将保留该标志。 Even if, it would only work for 16 bit int . 即使,它也只适用于16位int For >=32 bit int you have to replicate the sign manually into the upper bits, otherwise it just shifts the same data back and forth. 对于> = 32位int您必须手动将符号复制到高位,否则它将来回移动相同的数据。 In general, bitshifts of signed values are critical - see the [standard]( http://port70.net/~nsz/c/c11/n1570.html#6.5.7 for details. Some constellations invoke undefined behaviour. It is better to avoid them and just work with unsigned integers. 通常,有符号值的位偏移至关重要-有关详细信息,请参见[standard]( http://port70.net/~nsz/c/c11/n1570.html#6.5.7 。某些星座图调用未定义的行为。为了避免它们,只需使用无符号整数即可。

For most platforms, the following works, however. 但是,对于大多数平台而言,以下方法适用。 It is not necessarily slower (on platforms with 16 bit int , it is likely even faster): 它不一定较慢(在具有16位int平台上,它可能甚至更快):

uint16_t usample;
int16_t ssample;

ssample = (int16_t)usample;
if ( ssample & 0x800 )
    ssample |= ~0xFFF;

The cast to int16_t is implementation defined; int16_tint16_t是实现定义的; your compiler shall specify how it is performed. 您的编译器应指定如何执行。 For (almost?) all recent implementations no extra operation is performed. 对于(几乎?)所有最近的实现,都不会执行任何额外的操作。 Just verify in the generated code or your compiler documentation. 只需在生成的代码或编译器文档中进行验证即可。 The logical-or relies on intX_t using 2s complement which is guaranteed by the standard - as opposed to the standard types. 逻辑或依赖于intX_t使用2s补码,这是标准所保证的-与标准类型相反。

On 32 bit platforms, there might be an intrinsic instruction to sign-extend (eg ARM Cortex-M3/4 SBFX). 在32位平台上,可能存在用于扩展符号的内在指令(例如ARM Cortex-M3 / 4 SBFX)。 Or the compiler provides a builtin function. 或者编译器提供了内置函数。 Depending on your use-case and speed requirements, it might be suitable to use them. 根据您的用例和速度要求,可能适合使用它们。

Update: 更新:

An alternative approach would be using a bitfield structure: 另一种方法是使用位域结构:

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

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

This might result in using the same assembler instructions I proposed above. 这可能导致使用我上面建议的相同的汇编程序指令。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM