【问题标题】:assembly check if number is even组装检查数字是否为偶数
【发布时间】:2018-03-05 18:02:25
【问题描述】:

我有作业要编写汇编代码来检查数字是奇数还是偶数。我有这个代码

code_seg SEGMENT
    ASSUME cs:code_seg, ds:data_seg;

    mov ax, 11;
    test ax, 1;

end: jmp end;
code_seg ENDS

为了检查数字是否为偶数,我查看是否设置了零标志。我知道测试指令就像逻辑与,如果结果为 0,它会设置零标志。我的问题是:如何检查数字是奇数还是偶数?其实我不明白为什么一些偶数(二进制)数和(逻辑与)1 给出的结果为 0?

【问题讨论】:

  • 一个数字即使最低有效位为零。 0 & 1 = 0, 1 & 1 = 1.
  • 我猜是x86-assembly? test ax, 1 是,正如您已经知道的那样,等于 and ax, 1,但不会更改 ax 的值,只是标志。正如@Jester 所说:a 甚至是 ( a & 1 ) == 0。所以你现在要做的就是有条件的,例如jz isEvenjnz isNotEvensetz dx,你就完成了。你可以例如在命令行输出中打印 »number is even« 或 »number is not even«。
  • 这不是 logical and as && 在 C 中,而是 bitwise and as & 在 C 中。所以 @987654334当且仅当 ax 中的最低有效位设置为 1 时,@ 将是非零的。并且当解释为整数值的一部分时,将用作 2 的零次方,即 +1 到总整数值.它是唯一不能被二整除的(其他二的幂是)。请参阅 AJNeufeld 的答案,这是非常好的 IMO(除了关于 &&& 的缺失部分)。

标签: assembly x86


【解决方案1】:

如果设置了最低位,则无符号数和有符号数 (Two's complement) 都是奇数:

00000001 = 1    (odd)    // unsigned, or signed positive
11111111 = 255  (odd)    // unsigned
01111111 = 127  (odd)    // unsigned, or signed positive
10000001 = -127 (odd)    // signed negative
11111111 = -1   (odd)    // signed negative

所以test instruction

test al, 1

检查是否设置了 AL/AX/EAX/RAX 的最低位。如果是,则为奇数。
这可以使用Jcc instructions 进行检查,尤其是那些使用

测试?ZERO 标志的人
JNZ target    ; jump if odd  = lowest bit set
JZ  target    ; jump if even = lowest bit clear = zero

【讨论】:

  • test al,1 更小且同样高效。 (有趣的事实:test r/m32, sign_extended_imm8 没有编码,所以test eax, 1 浪费了多个字节)。
【解决方案2】:

一个(小)整数可以用二进制表示为b3 b2 b1 b0

b3 * 2^3  +  b2 * 2^2  +  b1 * 2^1  +  b0 * 2^0 =
b3 *  8   +  b2 *  4   +  b1 *  2   +  b0 *  1

其中所有bn 值要么为零要么为1。

获得奇数的唯一方法是最低有效位 (b0) 为 1。

将一个值与 1(二进制,0001)进行与运算会屏蔽除最低有效位 (b0) 以外的所有位 ...

            b3  b2  b1  b0
binary-AND   0   0   0   1
            --  --  --  --
             0   0   0  b0

... 如果最低有效位为零(偶数),则给出零值,如果最低有效位为一(奇数),则给出非零值(特别是 1)。

【讨论】:

    【解决方案3】:

    使用 SHIFT 操作将进位标志设置为等于最低有效位。如果进位为 1,则为奇数。

    对于小结局: 右移,检查进位

    对于大结局: 左移,检查进位

    据我所知,这适用于任何二进制 CPU,但有些 CPU 的移位操作和进位标志过于复杂,因此您必须确保您只将一位移位到单个 1 位寄存器

    【讨论】:

    • 字节顺序是关于如果你存储一个单词然后使用字节加载重新加载它的一个字节会发生什么。即一个字的字节布局在内存中不是关于字节内的位布局。而且在任何情况下,寄存器都没有真正的字节顺序(或者内存目标移位的工作方式与您加载到寄存器中,移位,然后存储移位结果相同)。 右移总是移出最低有效位。此外,代码已经展示了一种更有效且无损的测试方式x & 1,并询问如何这样可行。不是替代品。
    • 另外,不,转移到进位标志不适用于每个 CPU。有些,如 MIPS,根本没有标志。在 MIPS 上,您将使用 andi $t0, $a0, 1 / beq $zero, $t0, low_bit_zerosll $t0, $a0, 31 / bgez $t0, low_bit_zero。 (将位移到 MSB 位置,然后在 >= 0 的 tmp 寄存器上分支为 2 的补码有符号整数。)
    【解决方案4】:

    遗憾的是,我对汇编不是很有经验,但在伪代码中有一个名为“mod”的功能,它给出了除法的其余部分。

    看看这里:Assembly Language - How to Do Modulo?

    例如:

    x = 3 z = x mod 2

    z 为 1,如果 x 相等,则 z 为 0

    【讨论】:

    • 为什么要这样做,如果二进制编码的数字已经包含所有二的幂模只有一个 and 之外,就像问题中的 OP 一样? moddiv 操作成本很高,今天还不错,但如果你已经有了值,你不应该再计算它。 (我很想投反对票,但是当人们想要以非二次幂取模时,您的回答可能会很有用)。
    • 测试/提取低位是在汇编语言中进行模 2 的最佳方法。这个答案有点暗示使用div 指令,这将是一个可怕的选择,需要更多指令并且运行速度要慢很多倍。看我Collatz conjecture optimization answer的第一行。
    猜你喜欢
    • 1970-01-01
    • 2017-07-03
    • 2011-11-12
    • 2023-02-05
    • 1970-01-01
    • 2018-07-11
    • 1970-01-01
    • 2021-05-13
    • 2021-10-10
    相关资源
    最近更新 更多