负十六进制表示

Negative hexadecimal representation

我有一个问题是这样的:

 mov r15, 0x407116EF3867BBCA  
 sub r15, 0x95F67F70A1BCEE9D

What value is in r15 after this code executes?
(Be careful about integer overflow/underflows)

解决方案: python 3 用于调试的数学字符串:(i64 - i64_2) % (2**64) 当我按照这个公式解决这个问题时,我得到了 0xaa7a977e96aacd2d.

我怀疑答案应该是否定的,但我认为它已转换为 2 的补码。但如果是这样的话,还需要将其转换为 2 的补码吗?那么,我的思考方向是否正确?如果不是,请纠正我。另外,这个公式背后的逻辑是什么(取模 2^64)。

是的,在汇编中,一个 64 位寄存器值总是可以用 16 个十六进制数字表示。没有单独的 plus/minus 位; 最好把加/减这样的二元操作想成无符号的1,只担心最终结果的2's complement解释2.


这意味着使用像 Python 3 个整数或 calc aka apcalc 这样的任意精度计算器有点不方便,当减法下溢超过 0.

有两种方法可以考虑将结果固定为您想要的结果(在 [0 .. 2**64-1] 范围内):

  • Modulo 将其降低回该范围。 这就是 Python 中的 % 所做的。(不同于某些其他语言,如 C,或 x86-64 asm 中的 idiv 指令,Python % 给你模数,总是正数,而不是余数。例如 -1 % 21,但在 C 中对于 signed int 它是 -1.

    您甚至可以 手动 通过将 2**64 添加到负数来进行减少,以获得 2 的补码二进制表示。因为您知道任何加法或减法结果都将不小于 2**64 在该范围之外,所以只需要一次加法(或减法以进行进位加法)。

  • 按位将其截断为 64 位,取 Python 内部扩展精度 2 的补码表示的低 64 位。这取决于 Python 在内部使用 2 的补码,这可能是有保证的,并且在实践中肯定有效(大概至少当 Python 是 运行 在任何本身使用 2 的补码的正常系统上。)

三个都给出相同的正确结果。在交互式 Python 3.9 会话中:

>>> (0x407116EF3867BBCA - 0x95F67F70A1BCEE9D)
-6162446570153652947
>>> hex (0x407116EF3867BBCA - 0x95F67F70A1BCEE9D)
'-0x55856881695532d3'

>>> hex( (0x407116EF3867BBCA - 0x95F67F70A1BCEE9D) % (2**64) )
'0xaa7a977e96aacd2d'
>>> hex( (0x407116EF3867BBCA - 0x95F67F70A1BCEE9D) + 2**64 )
'0xaa7a977e96aacd2d'

>>> hex( (0x407116EF3867BBCA - 0x95F67F70A1BCEE9D) & (2**64-1) )
'0xaa7a977e96aacd2d'

此外,如果这应该是 x86-64,那 sub 不会 assemble:只有 mov 可以使用64 位立即数,并且 0x95F67F70A1BCEE9D 不适合(不能表示为)32 位符号扩展立即数。

但如果是这样,那么 CF 将被设置,因为从高位借位(因为 0x4... - 0x9... = 0xa... 绕过零:左手操作数减法在右侧下方没有符号)。

并且 OF 将被设置,因为在带符号的解释中(我们将 MSB 视为具有 - 2^63 而不是 + 2^63 的位值),正数减去较大的-magnitude 负数产生了如此大的正结果,以至于它溢出到负数。 (即 positive - negative = negative 意味着有符号溢出,就像 positive + positive = negative 一样)

根据结果的MSB,SF=1。


脚注 1:
一般加宽乘法和除法的高半部分,关心MSB的位值; add/sub 不要:2 的补码加法与无符号的二进制运算相同,包括回绕。这就是为什么 x86-64 只有一个 sub 指令,但有 dividiv,并且对于单操作数扩展乘法有 mul 与通常的 [=32= 分开].但是像 imul eax, r9d 这样的 imul 的非扩展形式对于有符号或无符号都是相同的。

脚注 2:
如果设置了高位,那么如果将位模式解释为 2 的补码有符号整数而不是无符号整数,则它为负。参见 Wikipedia's article about 2's complement。如果第一个十六进制数字是 8 到 F,则设置高位,因此在您的情况下 0x9...0xa... 表示负数,而 0x4... 表示正数。