向左和向右旋转以使用 C 中的 (signed short) 进行符号扩展
Rotate left and back to the right for sign extension with (signed short) cast in C
以前,我有以下 C 代码,我打算通过它在变量 'sample_unsigned' 的 'signed short' 强制转换后对变量 'sample' 进行符号扩展。
unsigned short sample_unsigned;
signed short sample;
sample = ((signed short) sample_unsigned << 4) >> 4;
在二进制表示中,我希望 'sample' 的最高有效位重复 4 次。例如,如果:
sample_unsigned = 0x0800 (corresponding to "100000000000" in binary)
我理解 'sample' 结果应该是:
sample = 0xF800 (corresponding to "1111100000000000" in binary)
然而,'sample' 总是以与 'sample_unsigned' 相同的方式结束,我不得不按如下方式拆分赋值语句,这很有效。这是为什么?
sample = ((signed short) sample_unsigned << 4);
sample >>= 4;
这是因为(signed short) sample_unsigned
由于整数提升而自动转换为int
作为操作数。
sample = (signed short)((signed short) sample_unsigned << 4) >> 4;
同样有效。
你的方法行不通。不保证右移会保留标志。即使,它也只适用于 16 位 int
。对于 >=32 位 int
,您必须手动将符号复制到高位,否则它只会来回移动相同的数据。一般来说,有符号值的位移很重要——详情请参阅[标准](http://port70.net/~nsz/c/c11/n1570.html#6.5.7。一些星座会调用未定义的行为。最好避免它们,只使用无符号整数。
但是,对于大多数平台,以下内容有效。它不一定更慢(在 16 位 int
的平台上,它可能更快):
uint16_t usample;
int16_t ssample;
ssample = (int16_t)usample;
if ( ssample & 0x800 )
ssample |= ~0xFFF;
转换为 int16_t
是实现定义的;您的编译器应指定它是如何执行的。对于(几乎?)所有最近的实现,都没有执行额外的操作。只需在生成的代码或您的编译器文档中进行验证。逻辑或依赖于 intX_t
使用标准保证的 2s 补码 - 而不是标准类型。
在 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;
这可能会导致使用我在上面建议的相同汇编器指令。
以前,我有以下 C 代码,我打算通过它在变量 'sample_unsigned' 的 'signed short' 强制转换后对变量 'sample' 进行符号扩展。
unsigned short sample_unsigned;
signed short sample;
sample = ((signed short) sample_unsigned << 4) >> 4;
在二进制表示中,我希望 'sample' 的最高有效位重复 4 次。例如,如果:
sample_unsigned = 0x0800 (corresponding to "100000000000" in binary)
我理解 'sample' 结果应该是:
sample = 0xF800 (corresponding to "1111100000000000" in binary)
然而,'sample' 总是以与 'sample_unsigned' 相同的方式结束,我不得不按如下方式拆分赋值语句,这很有效。这是为什么?
sample = ((signed short) sample_unsigned << 4);
sample >>= 4;
这是因为(signed short) sample_unsigned
由于整数提升而自动转换为int
作为操作数。
sample = (signed short)((signed short) sample_unsigned << 4) >> 4;
同样有效。
你的方法行不通。不保证右移会保留标志。即使,它也只适用于 16 位 int
。对于 >=32 位 int
,您必须手动将符号复制到高位,否则它只会来回移动相同的数据。一般来说,有符号值的位移很重要——详情请参阅[标准](http://port70.net/~nsz/c/c11/n1570.html#6.5.7。一些星座会调用未定义的行为。最好避免它们,只使用无符号整数。
但是,对于大多数平台,以下内容有效。它不一定更慢(在 16 位 int
的平台上,它可能更快):
uint16_t usample;
int16_t ssample;
ssample = (int16_t)usample;
if ( ssample & 0x800 )
ssample |= ~0xFFF;
转换为 int16_t
是实现定义的;您的编译器应指定它是如何执行的。对于(几乎?)所有最近的实现,都没有执行额外的操作。只需在生成的代码或您的编译器文档中进行验证。逻辑或依赖于 intX_t
使用标准保证的 2s 补码 - 而不是标准类型。
在 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;
这可能会导致使用我在上面建议的相同汇编器指令。