FPU предоставляет несколько инструкций для сравнения значений с плавающей точкой. Инструкции fcom, fcomp и fcompp сравнивают два значения на вершине стека и соответствующим образом устанавливают биты состояния.
fcom fcomp fcompp fcom st(i) fcomp st(i) fcom mem32 fcom mem64 fcomp mem32 fcomp mem64
Формы инструкций fcom
, fcomp
и fcompp
без операндов сравнивают ST(0) с ST(1) и соответственно устанавливают флаги FPU.
Кроме того, fcomp извлекает ST(0) из стека, а fcompp извлекает из стека ST(0) и ST(1).
fcom
и fcomp
в качестве операнда могут принимать регистр ST(i), который сравнивается с ST(0). fcomp
также извлекает значение ST(0) из стека после сравнения.
Инструкции fcom
и fcomp
могут принимать 32- и 64-битные переменные - числа с плавающей точкой, которые преобразуются в 80-битное значение повышенной точности, а затем сравнивают ST(0) с этим значением и соответствующим образом устанавливают биты состояния. fcomp
также извлекает значение ST(0) из стека после сравнения.
Эти инструкции устанавливают бит состояния C2, если два операнда несопоставимы (например, NaN).
Поэтому если при сравнении теоретически может участвовать недопустимое значение с плавающей точкой, то перед проверкой желаемого условия следует проверить флаг четности на наличие ошибки (например, с помощью инструкций
setp/setnp
или jp/jnp
).
Эти инструкции устанавливают бит ошибки стека, если на вершине стека регистров нет двух элементов.
Они устанавливают бит денормализованного исключения, если один или оба операнда денормализованы. Они устанавливают флаг недопустимой операции, если один или оба операнда являются NaN.
Эти инструкции всегда очищают код состояния C1.
Сразу после сравнения можно проверить биты кода состояния. Однако проблема состоит в том, что FPU не имеет инструкций для прямой проверки кодов состояния.
Вместо этого применяется инструкция fstsw для копирования регистра состояния FPU в регистр AX,
а затем инструкция sahf копирует регистр AH в биты кода состояния FLAGS: бит C0 компируется во флаг переноса CF, C2 - во флаг четности и C3 - во флаг нуля.
Флаг C1 не копируется. Кроме того, sahf
не копирует биты состояния FPU во флаг знака SF или флаг переполнения OF. Поэтому для проверки кодов состояния с помощью
инструкций x86-64 нельзя использовать инструкции, применяемые для сравнения двух чисел со знаком. Нужно применять только инструкции для беззнакового сравнения (несмотря на то, что числа с плавающей точкой сами по себе имееют знак).
Пример сравнения:
.data st0 real8 2.8 st1 real8 2.9 .code main proc fld st1 fld st0 fcompp ; сравниваем ST(0) и ST(1) fstsw ax ; данные регистра состояния загружаем в AX sahf ; загружаем данные из AX в регистр FLAGS setb al ; AL = true if st(0) < st(1) movzx eax, al ; проверяем значение AL ret main endp end
Здесь с помощью инстркции fcompp
сравниваются значения регистров ST(0) и ST(1). В ST(0) число 2.8, а в ST(1) - 2.9. После сравнения помещаем регистр состояния FPU в регистр обзего назначения AX:
fstsw ax
Далее загружаем флаги состояния, которые теперь в AH, в стандартный регистр состояния FLAGS
sahf
Далее, используя инструкции для беззнакового сравнения можно выполнить те или иные действия. В частности, здесь устанавливаем число 1 в регистр AL, если первое число при сравнении (ST(0)) меньше второго (ST(1))
setb al
В нашем случае первое число (2.8) меньше второго (2.9), поэтому в AL будет число 1
Другой пример:
.data st0 real8 2.8 st1 real8 2.1 .code main proc fld st1 fld st0 fcompp ; сравниваем ST(0) и ST(1) fstsw ax ; данные регистра состояния загружаем в AX sahf ; загружаем данные из AX в регистр FLAGS jnb set_10 ; если st(0) >= st(1) переход к set_10 mov eax, 5; jmp end_proc set_10: mov eax, 10 end_proc: ret main endp end
Здесь с помощью инструкции jnb сравниваем операнды, и если первый операнд (в данном случае ST(0)) больше или равен второму (в данном случае ST(1)), то переходим к метке set_10. Таким образом, в зависимости от сравнения в регистре EAX будет либо число 5, либо число 10.
Процессор x86-64 также предоставляют дополнительный набор инструкций сравнения с плавающей точкой, которые непосредственно влияют на флаги состояния x86-64 - fcomi и fcomip. Эти инструкции позволяют обойти необходимость использования fstsw и sahf для копирования вручную состояния FPU в регистр FLAGS. С другой стороны, fcomi и fcomip поддерживают только ограниченное число форм операндов: первый операнд - всегда регистр ST(0), который сравнивается регистром из второго операнда:
fcomi st(0), st(i) fcomip st(0), st(i)
На основе результата сравнения устанавливают соответствующие биты кода состояния FLAGS. Пример применения:
.data st0 real8 2.8 st1 real8 2.9 .code main proc fld st1 fld st0 fcomip st(0), st(1) ; сравниваем ST(0) и ST(1) setb al ; AL = 1 если st(0) < st(1) movzx eax, al ; проверяем значение AL ret main endp end
Стоит отметить, что fcomip
извлекает верхний элемент (ST(0)) из стека.
Инструкция ftst сравнивает значение на вершине стека с 0:
.data st0 real8 0.0 .code main proc fld st0 ftst fstsw ax sahf sete al ; AL = 1 если ST(0) = 0.0 movzx eax, al ; проверяем значение AL ret main endp end