How to check if a signed integer is neg or pos?
I am new to x86 assembly language, I have a signed integer saved in register eax
, and I want to check if the number is negative or positive. To do that, I used bt
instruction to check the first bit.
Here is what I did:
bt eax,0
jnc isNegative
bt
loads the first bit to carry flag, and I used jnc
to check if carry flag is 0 or 1.
If it's 1, it should be a negative number, and does negative instructions... however, the output is unpredictable, sometimes I have a positive and it recognize it as a negative number. Am I doing something wrong?
EDIT: I just realized it could have something to do with bit numbering. It is actually checking the开发者_运维知识库 least significant bit instead of the first bit. Let me try using bt eax, 31
The value is negative if the MSB is set. The SF (sign flag) in FLAGS is set according to that bit of the result.
test eax, eax ; set FLAGS according to the value in EAX; like cmp eax, 0
js negative ; jump if top bit was set in EAX
Alternatively since test reg,reg
sets FLAGS the same way as cmp reg,0
, you could use jl
to jump if the value is "less than zero". That's the same thing. Fun fact: this also works as a trick for optimizing unsigned if(eax >= 0x80000000U)
.
You can manually do it as a single-bit test (not taking advantage of SF) with a less efficient instruction (longer because it has to include the 32-bit constant.) This is a lot like your bt eax,31
but setting ZF=0 instead of CF=1 when the sign bit is set.
test eax, 0x80000000 ; set flags according to EAX & (1<<31)
jnz negative ; jump if that result was non-zero, i.e. bit was set
This works for any register size, byte (int8_t), word (int16_t), dword (int32_t), or qword (int64_t). For example with bytes:
test al, al
js is_negative
; or
test al, 80h
jne is_negative
If the value in a register was produced by something that sets FLAGS "according to the result", like most ALU instructions such as add ecx, edx
, you can js
to see if it's negative or not without needing to test ecx, ecx
first. SF is already set according to the MSB of the value in ECX, by the add
instruction.
Terminology: positive doesn't include zero
- positive is
x > 0
. Numbers 1 or greater, not including zero. (Sometimes used loosely as the opposite of negative, but strictly speaking it's not.) - non-negative is
x >= 0
. - negative is
x < 0
. - zero is not positive or negative. Its 2's complement bit-pattern is all-zero, no bits set. (x86 uses 2's complement signed integers, like all other modern CPUs. That's why there's no integer negative-zero.)
So if you want to find out if a number is positive or negative, that's two separate checks unless you already know it's non-zero. (Assuming you're using a strict definition of positive. Sometimes from context you can infer that someone means one condition, but precise terminology is useful in computing so prefer that yourself.)
Your bt
idea could work, but x86 numbers bits from bit 0 at the bottom (least significant), to bit 31 as the sign-bit of a 32-bit register like EAX. Also, CF set means negative, so jc negative
. But bt
isn't faster unless you actually want the condition in the carry flag for use with adc edx, 0
to do negcount += (x<0)
counting negative numbers or something. Alternatively, add eax,eax
or shl eax,1
will shift the top bit of EAX into CF (and also modify EAX, unlike BT).
You can use the jge
and cmp
instructions:
cmp eax, 0
jl isNegative
Your current solution checks if the number is even or odd, instead test the 31st bit:
bt eax, 31
jc number_is_negative
Or you can compare the number to zero, and test whether it's greater than or equal to it.
main:
...
cmp eax, 0 ; Compare the number to zero
jge .number_is_positive ; If it's positive jump to .number_is_positive
; And only arrive here if a number is negative
...
.number_is_positive:
; Yay! number is positive
...
精彩评论