How to check if a signed integer is neg or pos?

Yonk picture Yonk · Oct 25, 2010 · Viewed 39.6k times · Source

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

Answer

ruslik picture ruslik · Oct 25, 2010

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