Арифметические операции с векторами чисел с плавающей точкой

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

Расширения SSE/AVX предоставляют инструкции для паралелльных операций с плавающей точкой:

  • addps: складывает 4 дорожки операндов с 32-разрядными числами с плавающей точкой

  • addpd: складывает 2 дорожки операндов с 64-разрядными числами с плавающей точкой

  • vaddps: складывает 4 дорожки (128-битная версия) или 8 дорожек (256-битная версия) с 32-разрядными числами с плавающей точкой

  • vaddpd: складывает 2 дорожки (128-битная версия) или 4 дорожки (256-битная версия) с 64-разрядными числами с плавающей точкой

  • subps: вычитает 4 дорожки операндов с 32-разрядными числами с плавающей точкой

  • subpd: вычитает 2 дорожки операндов с 64-разрядными числами с плавающей точкой

  • vsubps: вычитает 4 дорожки (128-битная версия) или 8 дорожек (256-битная версия) с 32-разрядными числами с плавающей точкой

  • vsubpd: вычитает 2 дорожки (128-битная версия) или 4 дорожки (256-битная версия) с 64-разрядными числами с плавающей точкой

  • mulps: перемножает 4 дорожки операндов с 32-разрядными числами с плавающей точкой

  • mulpd: перемножает 2 дорожки операндов с 64-разрядными числами с плавающей точкой

  • vmulps: перемножает 4 дорожки (128-битная версия) или 8 дорожек (256-битная версия) с 32-разрядными числами с плавающей точкой

  • vmulpd: перемножает 2 дорожки (128-битная версия) или 4 дорожки (256-битная версия) с 64-разрядными числами с плавающей точкой

  • divps: делит 4 дорожки операндов с 32-разрядными числами с плавающей точкой

  • divpd: делит 2 дорожки операндов с 64-разрядными числами с плавающей точкой

  • vdivps: делит 4 дорожки (128-битная версия) или 8 дорожек (256-битная версия) с 32-разрядными числами с плавающей точкой

  • vdivpd: делит 2 дорожки (128-битная версия) или 4 дорожки (256-битная версия) с 64-разрядными числами с плавающей точкой

  • maxps: вычисляет максимальное в каждой из 4 пар чисел с плавающей точкой одинарной точности

  • maxpd: вычисляет максимальное в каждой из 2 пар чисел с плавающей точкой двойной точности

  • vmaxps: вычисляет максимальное в каждой из 4 (для 128-битной версии) или 8 (для 256-битной версии) пар чисел с плавающей точкой одинарной точности

  • vmaxpd: вычисляет максимальное в каждой из 2 (для 128-битной версии) или 4 (для 256-битной версии) пар чисел с плавающей точкой двойной точности

  • minps: вычисляет минимальное в каждой из 4 пар чисел с плавающей точкой одинарной точности

  • minpd: вычисляет минимальное в каждой из 2 пар чисел с плавающей точкой двойной точности

  • vminps: вычисляет минимальное в каждой из 4 (для 128-битной версии) или 8 (для 256-битной версии) пар чисел с плавающей точкой одинарной точности

  • vminpd: вычисляет минимальное в каждой из 2 (для 128-битной версии) или 4 (для 256-битной версии) пар чисел с плавающей точкой двойной точности

  • sqrtps: вычисляет квадратный корень для каждого из 4 чисел с плавающей точкой одинарной точности

  • sqrtpd: вычисляет квадратный корень для 2 чисел с плавающей точкой двойной точности

  • vsqrtps: вычисляет квадратный корень для каждого из 4 (для 128-битной версии) или 8 (для 256-битной версии) чисел с плавающей точкой одинарной точности

  • vsqrtpd: вычисляет квадратный корень для каждого из 2 (для 128-битной версии) или 4 (для 256-битной версии) чисел с плавающей точкой двойной точности

  • rsqrtps: вычисляет приблизительно обратный квадратный корень для каждого из 4 чисел с плавающей точкой одинарной точности

  • vrsqrtps: вычисляет приблизительно обратный квадратный корень для каждого из 4 (для 128-битной версии) или 8 (для 256-битной версии) чисел с плавающей точкой одинарной точности

Общий синтаксис инструкций на примере v(addps):

addps xmmdest, xmmsrc/mem128

vaddps xmmdest, xmmsrc1, xmmsrc2/mem128
vaddps ymmdest, ymmsrc1, ymmsrc2/mem256

Инструкции вычисления квадратного корня принимают один операнд:

sqrtps xmmdest, xmmsrc/mem128

vsqrtps xmmdest, xmmsrc/mem128
vsqrtps ymmdest, ymmsrc/mem256

Применение инструкций на примере сложения на Linux:

global main

extern printf

section .data
nums0 dd 1.1, 2.2, 3.3, 4.4
nums1 dd 1.2, 2.3, 3.4, 4.5
format_str db "%.1f, %.1f, %.1f, %.1f", 10, 0

section .text
main:
    sub rsp, 8       ; выравниваение по 16 байтам для функции printf
    movaps xmm0, [nums0]
    movaps xmm1, [nums1]
    vaddps xmm5, xmm0, xmm1  ; xmm5 = xmm0+xmm1

    ; выводим данные на консоль
    movss xmm0, xmm5    ; помещаем число dword в регистр для передачи в функцию printf
    cvtss2sd xmm0, xmm0   ; преобразуем dword в qword (double)
    psrldq xmm5, 4    ; сдвиг вправо на 4 байта для получения следующего числа
    movss xmm1, xmm5
    cvtss2sd xmm1, xmm1
    psrldq xmm5, 4
    movss xmm2, xmm5
    cvtss2sd xmm2, xmm2
    psrldq xmm5, 4
    movss xmm3, xmm5
    cvtss2sd xmm3, xmm3

    mov rdi, format_str
    call printf

    add rsp, 8
    ret

Здесь складываются два вектора 32-разрядных чисел nums0 и nums1. Для упрощения тестирования для вывода результата на консоль здесь используется функция printf. Компиляция и консольный вывод программы:

root@Eugene:~/asm# nasm -f elf64 hello.asm -o hello.o
root@Eugene:~/asm# gcc -static  hello.o -o hello
root@Eugene:~/asm# ./hello
2.3, 4.5, 6.7, 8.9
root@Eugene:~/asm#

Аналогичный пример на Windows:

global main

extern printf

section .data
nums0 dd 1.1, 2.2, 3.3, 4.4
nums1 dd 1.2, 2.3, 3.4, 4.5
format_str db "%.1f, %.1f, %.1f, %.1f", 10, 0

section .text
main:
    sub rsp, 40       ; выравниваение по 16 байтам для функции printf
    movaps xmm0, [rel nums0]
    movaps xmm1, [rel nums1]
    vaddps xmm5, xmm0, xmm1  ; xmm5 = xmm0+xmm1

    ; выводим данные на консоль
    movss xmm1, xmm5
    cvtss2sd xmm1, xmm1
    movq rdx, xmm1

    psrldq xmm5, 4
    movss xmm2, xmm5
    cvtss2sd xmm2, xmm2
    movq r8, xmm2

    psrldq xmm5, 4
    movss xmm3, xmm5
    cvtss2sd xmm3, xmm3
    movq r9, xmm2

    psrldq xmm5, 4
    movss xmm4, xmm5
    cvtss2sd xmm4, xmm4
    movq [rsp+32], xmm4

    mov rcx, format_str
    call printf

    add rsp, 40
    ret
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850