c - 如何在 C 中将不同大小的数字签名扩展到 16 位?

标签 c bit-manipulation sign-extension

可变大小的数字可以是 10 位、12 位或 15 位。我想对立即数位进行符号扩展,以便 16 位整数具有与 10、12 或 15 位值相同的值。

编辑

很抱歉,我没有发布我编写的代码,那是行不通的。在这里:

switch (instr):
{
    case IMMI_10:
    int bits_to_cover = 16 - 10;
    signed short amount = (instr & 15);
    if (amount & (1 << bits_to_cover >> 1))
    {
        amount -= 1 << bits_to_cover;
    }
    break;


    case IMMI_12:
    int bits_to_cover = 16 - 12;
    signed short amount = (instr & 15);
    if (amount & (1 << bits_to_cover >> 1))
    {
        amount -= 1 << bits_to_cover;
    }
    break;


    case IMMI_15:
    int bits_to_cover = 16 - 15;
    signed short amount = (instr & 15);
    if (amount & (1 << bits_to_cover >> 1))
    {
        amount -= 1 << bits_to_cover;
    }
    break;
}

这是在为我在学校的 CSE 类(class) build 的特殊机器上运行的。它没有运行 x86 架构。它被称为 CSE 410 机器。文档:https://courses.cs.washington.edu/courses/cse410/18sp/410machine/isaManual.html

最佳答案

我的工作基础是,例如,如果您有一个 10 位数字要符号扩展为 16 位,那么您需要考虑两种情况:

zzzzzz1x xxxxxxxx
zzzzzz0x xxxxxxxx

结果中的 x 是“不关心 — 必须复制”位。前导 z 是“不关心 — 将覆盖”位。在两个示例中,在 0 和 1 之间切换的位是符号位,必须将其复制到显示为前导 z 的位置上。我还假设这些位从 0(最低有效位或 LSB)到 15(最高有效位或 MSB)编号。如果您需要从 1 到 16 对位进行编号,则需要进行一些调整。

给定一个函数签名:

uint16_t sign_extend(uint16_t value, uint16_t bits)

我们可以通过以下方式确定符号位:

uint16_t sign = (1 << (bits - 1)) & value;

我们可以用正 (0) 符号位对值进行 0 扩展,并用位掩码对值进行运算:

00000001 11111111

我们可以通过将值与位掩码进行或运算来对具有负 (1) 符号位的值进行 1 扩展:

11111110 00000000

在下面的代码中,我生成了第二个掩码:

uint16_t mask = ((~0U) >> (bits - 1)) << (bits - 1);

并使用按位反转生成另一个。

代码避免假设右移负值时会发生什么。 (参见 commentsamgak。)C 标准说这是实现定义的行为,通常的情况是“将 MSB(符号)位复制到腾出的位中”(也称为算术右移)或“设置腾出的位为零'(又名逻辑右移)。两者都是允许的,但给定的编译器必须使用其中之一。无论编译器做什么,这段代码都可以工作,因为它避免了右移带符号的数量。 (为了弥补这一点,它假定您可以将有符号整数分配给相应的无符号整数类型,反之亦然,即使带符号的值为负数。形式上,该标准只要求对值的公共(public)子集起作用——从0 到 <signed-type>_MAX ,但我没有听说过有这个问题的系统,而我听说过有不同处理换档的系统。)

将它们放在一起,这是我在测试工具中使用的函数:

#include <assert.h>
#include <stdint.h>

extern uint16_t sign_extend(uint16_t value, uint16_t bits);

uint16_t sign_extend(uint16_t value, uint16_t bits)
{
    assert(bits > 0 && bits < 16);
    uint16_t sign = (1 << (bits - 1)) & value;
    uint16_t mask = ((~0U) >> (bits - 1)) << (bits - 1);
    if (sign != 0)
        value |= mask;
    else
        value &= ~mask;
    return value;
}

#ifdef TEST

#include <stdio.h>

struct TestSignExtend
{
    uint16_t    value;
    uint16_t    bits;
    uint16_t    result;
};

static int test_sign_extend(const struct TestSignExtend *test)
{
    uint16_t result = sign_extend(test->value, test->bits);
    const char *pass = (result == test->result) ? "** PASS **" : "== FAIL ==";
    printf("%s: value = 0x%.4X, bits = %2d, wanted = 0x%.4X, actual = 0x%.4X\n",
            pass, test->value, test->bits, test->result, result);
    return(result == test->result);
}

int main(void)
{
    struct TestSignExtend tests[] =
    {
        { 0x0123, 10, 0x0123 },
        { 0x0323, 10, 0xFF23 },
        { 0x0323, 11, 0x0323 },
        { 0x0723, 11, 0xFF23 },
        { 0x0323, 12, 0x0323 },
        { 0x0C23, 12, 0xFC23 },
        { 0x0323, 13, 0x0323 },
        { 0x1723, 13, 0xF723 },
        { 0x1323, 14, 0x1323 },
        { 0x3723, 14, 0xF723 },
        { 0x0323, 15, 0x0323 },
        { 0xC723, 15, 0xC723 },
        { 0x0123,  9, 0xFF23 },
        { 0x0223,  9, 0x0023 },
        { 0x0129,  8, 0x0029 },
        { 0x03E9,  8, 0xFFE9 },
    };
    enum { NUM_TESTS = sizeof(tests) / sizeof(tests[0]) };
    int pass = 0;

    for (int i = 0; i < NUM_TESTS; i++)
        pass += test_sign_extend(&tests[i]);
    if (pass == NUM_TESTS)
        printf("PASS - All %d tests passed\n", NUM_TESTS);
    else
        printf("FAIL - %d tests failed out of %d run\n", NUM_TESTS - pass, NUM_TESTS);

    return(pass != NUM_TESTS);  /* Process logic is inverted! */
}

#endif /* TEST */

示例输出:

** PASS **: value = 0x0123, bits = 10, wanted = 0x0123, actual = 0x0123
** PASS **: value = 0x0323, bits = 10, wanted = 0xFF23, actual = 0xFF23
** PASS **: value = 0x0323, bits = 11, wanted = 0x0323, actual = 0x0323
** PASS **: value = 0x0723, bits = 11, wanted = 0xFF23, actual = 0xFF23
** PASS **: value = 0x0323, bits = 12, wanted = 0x0323, actual = 0x0323
** PASS **: value = 0x0C23, bits = 12, wanted = 0xFC23, actual = 0xFC23
** PASS **: value = 0x0323, bits = 13, wanted = 0x0323, actual = 0x0323
** PASS **: value = 0x1723, bits = 13, wanted = 0xF723, actual = 0xF723
** PASS **: value = 0x1323, bits = 14, wanted = 0x1323, actual = 0x1323
** PASS **: value = 0x3723, bits = 14, wanted = 0xF723, actual = 0xF723
** PASS **: value = 0x0323, bits = 15, wanted = 0x0323, actual = 0x0323
** PASS **: value = 0xC723, bits = 15, wanted = 0xC723, actual = 0xC723
** PASS **: value = 0x0123, bits =  9, wanted = 0xFF23, actual = 0xFF23
** PASS **: value = 0x0223, bits =  9, wanted = 0x0023, actual = 0x0023
** PASS **: value = 0x0129, bits =  8, wanted = 0x0029, actual = 0x0029
** PASS **: value = 0x03E9, bits =  8, wanted = 0xFFE9, actual = 0xFFE9
PASS - All 16 tests passed

我确实在其中一个测试中故意犯了一个错误,在一切都先通过之后,只是为了确保发现失败。

在您的代码中,您可以像这样使用它:

signed short value = …;

switch (instr):
{
case IMMI_10:
    value = sign_extend(value, 10);
    break;
case IMMI_12:
    value = sign_extend(value, 12);
    break;
case IMMI_15:
    value = sign_extend(value, 15);
    break;
default:
    assert(0 && "can't happen!");
}

如果大小写标签IMMI_10 , IMMI_12IMMI_15具有值 10 , 12 , 15 ,那么您可以避免切换并简单地使用赋值:

signed short value = …;   // Get the value from somewhere
value = sign_extend(value, instr);

关于c - 如何在 C 中将不同大小的数字签名扩展到 16 位?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49836143/

相关文章:

c - 为什么 scanf ("%c\n") 在 stdin 中有延迟

c++ - Char 数组无法将字符保存为其成员。使用ATmega8的密码系统

perl - 如何隔离整数中最左边的字节

assembly - 奥利数据库 : sign extending with Movsx

python - PyImport_Import 失败 - 返回 NULL

c - 使用 gethostbyname 函数

c - 如何在c中将所有位向左旋转并将最后一位向右旋转

c# - 如何清除最高有效位?

java - 算术右移存在哪些实际用例?