Assembly 绝对值,不带分支或移位,仅加/减和布尔值
我们在学校遇到这个问题是为了那些想测试自己的学生。我在这方面花了相当长的时间,但还是搞不懂 AX寄存器中有16位数字,该数字是有符号的。得到它 绝对值,AX中的数字必须保持不变 (编辑:没有限定的寄存器数量,AX寄存器可以更改,但在函数结束时,它需要是原始编号),答案是 应该是BX。您只能使用以下说明:Assembly 绝对值,不带分支或移位,仅加/减和布尔值,assembly,x86,bit-manipulation,absolute-value,Assembly,X86,Bit Manipulation,Absolute Value,我们在学校遇到这个问题是为了那些想测试自己的学生。我在这方面花了相当长的时间,但还是搞不懂 AX寄存器中有16位数字,该数字是有符号的。得到它 绝对值,AX中的数字必须保持不变 (编辑:没有限定的寄存器数量,AX寄存器可以更改,但在函数结束时,它需要是原始编号),答案是 应该是BX。您只能使用以下说明: MOV、ADD、XOR、SUB、NOT和或NEG 像编译器那样使用SAR是很容易的,但是如果没有它,就不清楚如何获得任何以符号位为条件的行为。愚蠢的想法#1:查找表。这在16位实模式下无法工作。
MOV、ADD、XOR、SUB、NOT和或NEG 像编译器那样使用SAR是很容易的,但是如果没有它,就不清楚如何获得任何以符号位为条件的行为。愚蠢的想法#1:查找表。这在16位实模式下无法工作。即使一张桌子有一整段64kiB的内存也是不够的;我们需要两倍于此才能查找任何可能的16位值的2字节结果 我们可以通过32位寻址轻松实现这一点,如
xor ebx,ebx
/mov bx,ax
/mov bx,[table+ebx*2]
,如果您可以证明128kiB的表数据是合理的:P
完全按照规则,您可以使用
sub esp,1以32位或64位模式在堆栈上构建表。因此,多亏了Peter Cordes的回答,代码非常简单,问题在于SAR指令,但Peter创建得非常好
中的号码已加载到AX
; this is practicaly the SAR instruction,
; the mask for the absolute value is
; number >> (sizeof(short)) * CHAR_BIT -1)
; number >> (2 * 8) - 1 = 15
; so normaly we would do SAR bx, 15 and done
mov bl, ah ; BX = AX>>8
add bx, bx ; BX <<= 1
neg bh ; 0 or -1
mov bl, bh ; duplicate the full BX
mov cx, ax ;
add cx, bx ; the number + mask
xor bx, cx ; (number+mask) ^ mask
;这是实际的SAR指令,
; 绝对值的掩码为
; 数字>>(sizeof(short))*字符位-1)
; 编号>>(2*8)-1=15
; 正常情况下,我们会做SAR bx,15,完成
mov bl,啊,;BX=AX>>8
添加bx,bx;BX最小值-2^16的绝对值不能用16位表示?@qwr:该问题的解决方案是输入是有符号的,结果是无符号的。因此,0x8000
(32768
)是输入0x8000
(-32768
)的正确绝对值结果。这在asm中很容易,在C中很麻烦,您可能需要编写类似于(x<0)的东西?0U-x:(无符号)x代码>以避免导致签名溢出。但这会在减法之前将x
转换为无符号,因此它可能只适用于2的补码。IDK,C有时真的很难使用。我们的教授每学期都会发这个,他说在过去的10年里,他在那里只有7个人能做到,每个学期大约有800名新生。我用SAR做的很简单:mov-ax,[esp+8]mov-bx,ax-SAR-bx,15-mov-cx,ax-add-cx,bx-xor-bx,cx
这篇文章被编辑成这样,我相信你可以使用两个以上registers@qwr:是的,我注意到了;这使得第二部分不是一个挑战。彼得·科德斯:对不起,我不确定,所以我不得不问教授,没有具体说明它是否启用。我对逐位运算不太熟悉,所以你的解释很有帮助,再次感谢。@AdamĎuriník:更新,很高兴听到没有我丢失的任何划痕空间的方法。7人超过10年听起来对一年级的学生来说是合适的,他们大多是asm的初学者!感谢您将其传递给我们。“通常”您将cwd
签名将AX扩展为DX:AX,即设置DX=0或-1。无论如何,在问题的第一个版本中,我认为我们不允许使用任何其他寄存器,否则不修改AX的限制是微不足道的。
neg dh ; 0 or -1 according to sign bit of AX
mov dl, dh ; duplicate to the full DX = 0 or -1
mov bx, ax
xor bx, dx ; ~x or x
sub bx, dx ; ~x + 1 or x
unsigned absval(int x) {
return x<0 ? 0U - x : x;
}
mov eax, edi
cdq ; sign-extend EAX into EDX:EAX
xor eax, edx
sub eax, edx
ret
mov eax, edi
neg eax ; 0 - x
cmovl eax, edi ; copy the original if 0 was < x
ret
; shorter critical path on CPUs where mov is not zero latency
xor eax, eax
sub eax, edi ; 0 - x
cmovl eax, edi ; copy the original if 0 was < x
ret
; this is practicaly the SAR instruction,
; the mask for the absolute value is
; number >> (sizeof(short)) * CHAR_BIT -1)
; number >> (2 * 8) - 1 = 15
; so normaly we would do SAR bx, 15 and done
mov bl, ah ; BX = AX>>8
add bx, bx ; BX <<= 1
neg bh ; 0 or -1
mov bl, bh ; duplicate the full BX
mov cx, ax ;
add cx, bx ; the number + mask
xor bx, cx ; (number+mask) ^ mask