Для сравнения отдельных чисел с плавающей точкой расширения SSE предоставляют набор инструкций сравнения для конкретных условий, которые сохраняют значение true (все биты 1) или false (все биты 0) во втором операнде:
cmpss imm8, xmmm/mem32, xmmn cmpsd imm8, xmmm/mem64, xmmn cmpeqss xmmm/mem32, xmmn ; операнды равны cmpltss xmmm/mem32, xmmn ; первый операнд меньше второго cmpless xmmm/mem32, xmmn ; первый операнд меньше или равен второму cmpunordss xmmm/mem32, xmmn ; неупорядоченное сравнение (один из операндов или оба равны NaN) cmpneqss xmmm/mem32, xmmn ; операнды не равны cmpnltss xmmm/mem32, xmmn ; первый операнд НЕ меньше второго cmpnless xmmm/mem32, xmmn ; первый операнд НЕ меньше или равен второму cmpordss xmmm/mem32, xmmn ; упорядоченное сравнение cmpeqsd xmmm/mem64, xmmn ; операнды равны cmpltsd xmmm/mem64, xmmn ; первый операнд меньше второго cmplesd xmmm/mem64, xmmn ; первый операнд меньше или равен второму cmpunordsd xmmm/mem64, xmmn ; неупорядоченное сравнение (один из операндов или оба равны NaN) cmpneqsd xmmm/mem64, xmmn ; операнды не равны cmpnltsd xmmm/mem64, xmmn ; первый операнд НЕ меньше второго cmpnlesd xmmm/mem64, xmmn ; первый операнд НЕ меньше или равен второму cmpordsd xmmm/mem64, xmmn ; упорядоченное сравнение
Первые две инструкции могут в качестве первого операнда (imm8) принимать значение от 0 до 7, которое указывает на принцип сравнения:
0: Первый операнд == второй операнд
1: Первый операнд < второй операнд
2: Первый операнд <= второй операнд
3: Неупорядоченное сравнение
4: Первый операнд ≠ второй операнд
5: Первый операнд не меньше второго операнда (>=)
6: Первый операнд не меньше или равен второму операнду (>)
7: Упорядоченное сравнение
Эти инструкции устанавливают в 0 (неверно, ложь) или 1 (верно, истина) все биты в последнем операнде. После сравнения значение из регистра XMM можно поместить в регистр общего назначения и проверить этот регистр на наличие нуля/не нуля. Для этого можно использовать инструкции movq или movd:
.globl _start .data num1: .double 3.4 num2: .double 3.4 .text _start: movsd num1, %xmm0 # помещаем в xmm0 число num1 movsd num2, %xmm1 # помещаем в xmm1 число num2 cmpeqsd %xmm1, %xmm0 # равны ли операнды movq %xmm0, %rdi movq $60, %rax syscall
Здесь помещаем в XMM0 и XMM1 два числа .double
и инструкцией cmpeqsd
сравниваем их на равенство. Если они равны, то в %xmm0 все биты будут равны 1,
если нет - то 0. Для проверки значения помещаем его в регистр %rdi. Поскольку при равестве все биты будут установлены в 1, то фактически в десятичной системе это будет число -1.
Получив результат проверки в регистр %rdi, мы можем использовать уже стандартные инструкции перехода, чтобы определить условные конструкции:
.data num1: .double 3.5 num2: .double 3.6 str_equal: .asciz "Equal\n" str_equal_len= . - str_equal str_notequal: .asciz "Not equal\n" str_notequal_len= . - str_notequal .text _start: movsd num1, %xmm0 # помещаем в xmm0 число num1 movsd num2, %xmm1 # помещаем в xmm1 число num2 cmpeqsd %xmm1, %xmm0 # равны ли операнды movq %xmm0, %rdi test %rdi, %rdi # проверяем на равенство - на единицы jnz equal # если xmm0 == xmm1 movq $str_notequal, %rsi # адрес строки movq $str_notequal_len, %rdx # размер строки jmp exit equal: movq $str_equal, %rsi # адрес строки movq $str_equal_len, %rdx # размер строки exit: movq $1, %rax # номер системной функци movq $1, %rdi # дескриптор стандартного (консольного) вывода syscall # выполняем системный вызов movq $60, %rax syscall
Здесь по сравнению с предыдущим примером добавили проверку на 0 с помощью инструкции test - если результат этой инструкии равен 0, то будет установлен
флаг нуля ZF, который мы можем проверить с помощью инструкции jnz
- если флаг не установлен (то есть по сути если xmm0 и xmm1 были равны),
то далее выполняем переход на метку equal. И в зависимости от результата выводим на консоль то или иное сообщение.