Сравнение чисел с плавающей точкой в FPU

Последнее обновление: 02.07.2023

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

Инструкция 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
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850