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).