【问题标题】:reversing two's complement for 18bit int反转 18 位 int 的二进制补码
【发布时间】:2018-08-17 01:59:17
【问题描述】:

我有一个二进制补码的 18 位整数,我想将其转换为有符号数,以便更好地使用它。在我使用的平台上,整数是 4 个字节(即 32 位)。基于这篇文章:

Convert Raw 14 bit Two's Complement to Signed 16 bit Integer

我尝试了以下方法来转换数字:

using SomeType = uint64_t;
SomeType largeNum = 0x32020e6ed2006400;
int twosCompNum = (largeNum & 0x3FFFF);
int regularNum = (int) ((twosCompNum << 14) / 8192);

我将数字左移 14 位以将符号位作为最高有效位,然后除以 8192(在二进制中,它是 1 后跟 13 个零)以恢复幅度(如上面的帖子中所述)。但是,这似乎对我不起作用。例如,输入 249344 给我 -25600,这从表面上看似乎不正确。我做错了什么?

【问题讨论】:

  • twosCompNum是什么类型?
  • twosCompNum 是一个 int,但它是一个 uint64_t 的低 18 位。
  • 将这些操作拆分为它们自己的代码行。使用调试器逐步完成。应该很明显发生了什么。
  • 您也可以使用按位运算来观察符号位并重建您的数字。
  • 249344 对于 18 位 2s 补码整数来说太大了,只能在 -131072 .. 131071 范围内。

标签: c++ twos-complement


【解决方案1】:

几乎可移植的方法(假设负整数本身是 2s 补码)是简单地检查第 17 位,并使用它有条件地屏蔽符号位:

constexpr SomeType sign_bits = ~SomeType{} << 18;
int regularNum = twosCompNum & 1<<17 ? twosCompNum | sign_bits : twosCompNum;

请注意,这不取决于您的 int 类型的大小。

【讨论】:

    【解决方案2】:

    常量8192不对,应该是16384 = (1&lt;&lt;14)

    int regularNum = (twosCompNum << 14) / (1<<14);
    

    这样,答案是正确的,-12800

    这是正确的,因为输入(无符号)数字是 249344 (0x3CE00)。它设置了最高位,因此它是一个负数。我们可以通过从中减去“最大无符号值+1”来计算其有符号值:0x3CE00-0x40000=-12800

    请注意,如果您在一个平台上,对于哪个右符号移位执行正确的操作(例如在 x86 上),那么您可以避免除法:

    int regularNum = (twosCompNum << 14) >> 14;
    

    如果编译器没有注意到除法可以被移位完全替换(clang 7 注意到,但 gcc 8 没有),此版本可能会稍微快一些(但具有实现定义的行为)。

    【讨论】:

    • 对于否定的aa &gt;&gt; b 的值是实现定义的,因此请仔细阅读您要使用的每个编译器的规范。
    【解决方案3】:

    两个问题:首先您的测试输入不是 18 位二进制补码数。使用n 位,二进制补码允许-(2 ^ (n - 1)) &lt;= value &lt; 2 ^ (n - 1)。如果是 18 位,则为 -131072 &lt;= value &lt; 131071。你说你输入了249344,它超出了这个范围,实际上会被解释为-12800

    第二个问题是你的二的幂是关闭的。在您引用的答案中,提供的解决方案采用以下形式

    mBitOutput = (mBitCast)(nBitInput << (m - n)) / (1 << (m - n));
    

    对于您的特定问题,您希望

    int output = (nBitInput << (32 - 18)) / (1 << (32 - 18));
    // or equivalent
    int output = (nBitInput << 14) / 16384;
    

    试试这个。

    【讨论】:

    • 除以 16384 不起作用。您总是需要算术右移来进行符号扩展,因为除法将向零舍入。例如 -5/2 = -2 但 -5 >> 1 = -3
    • @phuclv:会的,因为除法结果不需要四舍五入。
    • @geza 没有。你总是需要四舍五入,无论是零,最接近的 inf 还是其他。 C++ 标准要求结果四舍五入为零。但是算术右移会向负 inf 舍入,这会使结果不同
    • @phuclv:我的意思是,除法结果的余数为零。
    • @geza 我明白了。因为您已经完成了左移,所以低 14 位将为零。但是compilers don't realized that and you'll still get worse assembly output 如果你使用除法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-01
    • 2011-04-18
    • 2021-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多