2

I've got a problem according to subject.

In xmm0 register I have a value, e.g. -512.000000
And in xmm4: 0.000000.

I try to compare the first value with zero and I cannot really achieve this.

comisd xmm0, xmm4

COMISD instruction sets flags in a strange way, and only jnz after that works properly in my code.

How can I do this comparison?

phuclv
  • 37,963
  • 15
  • 156
  • 475
formateu
  • 157
  • 2
  • 17

3 Answers3

8

Intel's manual documents how COMISD sets flags.

First, check for unordered with a jp (if you want your code to work properly for NaN inputs).

Then you can use any of the ja / jae / jb / jbe (above / below) conditions or their negatives (jna, etc), or je / jne (equal / not-equal). These are the same conditions as for unsigned integer compares. And obviously cmovcc and setcc work too.

These conditions have synonyms like jc (jump if CF==1), but above/below have the right semantic meaning, so can reduce the amount of comments necessary to make your code human-readable.

You can skip the jp before a condition like ja because CF=0 implies PF=0. (i.e. the a condition will be false if the operands were unordered).

Some other conditions, like b (below: CF=1) or be (CF=1 or ZF=1), will be true for unordered operands, so you need to branch on jp before or after jb / jbe if you need to rule out NaN inputs.

You can reverse the compare operands to allow for a ja instead of jbe (for example) and group the unordered case with the other "way", if I have the logic right.


historical interest / why it was designed this way:

Note that comisd's flag-setting matches what you get from x87 fcom / fnstsw ax / sahf. See also this x87 tutorial/guide for an example of using that sequence. But only for historical interest! fcomi, which also sets the flags the same way, has been around for over 20 years (P6), and is faster. (See the tag wiki for more links).

x87 is only useful for 80 bit extended precision, since it's generally safe to assume that SSE2 is available, even in 32bit mode.

See also Why do x86 FP compares set CF like unsigned integers, instead of using signed conditions? for a more detailed look at why x87 used those flags, which fcomi and SSE maintained compatibility with.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • My understanding is: jae=jnc (it only tests CF=0), so it will be false if an operand is nan. ja is equivalent to CF=0 and ZF=0, so it is also false in case of nan. So we can use both of these and respect the IEEE754 standards. For jb and jbe, we can swap the arguments and replace with ja and jae (a < b <=> b > a) and thus avoid a jp. – aka.nice Sep 06 '19 at 16:16
  • @aka.nice. Thanks for catching that. I think reversing the compare operands does allow grouping the unordered case with the other "way", so added a mention of that too. – Peter Cordes Sep 15 '19 at 03:07
4

Here you have Intel's IA-32 manual. On page 689 is COMISD description and it states:

RESULT ← OrderedCompare(DEST[63:0] ≠ SRC[63:0]) {
(* Set EFLAGS *) CASE (RESULT) OF
    UNORDERED:    ZF,PF,CF ← 111;
    GREATER_THAN: ZF,PF,CF ← 000;
    LESS_THAN:    ZF,PF,CF ← 001;
    EQUAL:        ZF,PF,CF ← 100;
ESAC;
OF, AF, SF ← 0; }

Also on page 178 there is description which jump instruction is based on which flag of EFLAGS regiseter (EFLAGS is described on page 78).

JC (jump if carry) checks only if CF flag is set - if yes it can be less then relation or unordered.

EDIT: - deleted wrong answer not to mislead anybody leaving links to manual.

Koikos
  • 162
  • 9
  • 1
    I didn't really read this carefully before upvoting. Your suggestion to move EFLAGS to EAX is a bad idea, and your code doesn't even work. You can't `mov` to/from EFLAGS. There's `lahf` (load ah from flags), which includes all the condition flags except overflow. But if you're going to use multiple instructions to test flags, just `jp` to rule out NaN and then use the normal conditions. – Peter Cordes Jun 12 '16 at 07:26
  • @Peter I have to agree with you. Your solution is far more clear in concept and proper way to do that comparison. As excuse for my wrong answer I can only say I've written it in the middle of the night. Sorry everyone for inconvenience. Maybe links to manual will be use full for somebody. – Koikos Jun 12 '16 at 07:33
  • You can still edit it to improve it! Maybe just keep the quotes from the manual and a bit of text. (i.e. take out all the wrong parts). I've written a few totally wrong answers before and had to make major edits to fix them :P – Peter Cordes Jun 12 '16 at 07:35
2

A usual problem with COMISD is that it sets the flags in a slightly different fashion than an ordinary comparison. So, COMISD would set as below :

greater than : ZF:0, PF:0, CF:0

equal to : ZF:1, PF:0, CF:0

less than : ZF:0, PF:0, CF :1

Therefore, to measure if XMM0 is less than XMM4, you need to do a JC.

Trust this helps.

quasar66
  • 555
  • 4
  • 14