Расширения SSE/AVX/AVX2 предоставляют ряд инструкций для преобразования чисел с плавающей точкой в целые числа и наоборот. Подобные инструкции могут преобразовывать как одно скалярное значение, так и вектор значений.
Для преобразования одного числа в другое имеются следующие инструкции:
cvtsd2si: преобразует 64-разрядное число с плавающей точкой в 32- или 64-битное целое число. Для округления применет режим, установленный в регистре MXCSR. Результат сохраняется в 32- или 64-битном регистре общего назначения.
cvtsd2si reg32/64, xmmn/mem64
cvtsd2ss: преобразует 64-разрядное число с плавающей точкой (в регистре XMM или переменной) в число 32-разрядное число с плавающей точкой и оставляет результат в втором операнде - регистре XMM. Для округления применет режим, установленный в регистре MXCSR.
cvtsd2ss xmmn, xmmn/mem64
cvtsi2sd: преобразует 32- или 64-разрядное целое число из регистра общего назначения или переменной в 64-разрядное число с плавающей точкой, оставляя результат в регистре XMM.
cvtsi2sd xmmn, reg32/reg64/mem32/mem64
cvtsi2ss: преобразует 32- или 64-разрядное целое число в целочисленном регистре или переменной в 32-разрядное число с плавающей точкой, оставляя результат в регистре XMM.
cvtsi2ss xmmn, reg32/reg64/mem32/mem64
cvtss2sd: преобразует 32-разрядное число с плавающей точкой из регистра XMM или переменной в 64-разрядное число с плавающей точкой, оставляя результат в регистре XMM в первом операнде.
cvtss2sd xmmn, xmmn/mem32
cvtss2si: преобразует 32-разрядное число с плавающей точкой из регистра XMM или переменной в целое число и оставляет результат в 32- или 64-битном регистре общего назначения. Для округления применет режим, установленный в регистре MXCSR.
cvtss2si reg32/reg64, xmmn/mem32
cvttsd2si: преобразует 64-разрядное число с плавающей точкой в 32- или 64-битное целое число. Преобразование выполняется с использованием усечения (не использует биты управления округлением в MXCSR). Результат сохраняется в 32- или 64-битном регистре общего назначения.
cvttsd2si reg32/64, xmmn/mem64
cvttss2si: преобразует 32-разрядное число с плавающей точкой в 32- или 64-битное целое число. Преобразование выполняется с использованием усечения (не использует биты управления округлением в MXCSR). Результат сохраняется в 32- или 64-битном регистре общего назначения.
cvttss2si reg32/reg64, xmmn/mem32
Стоит отметить, что названиях всех инструкций следует определенному шаблону. Например, cvtsi2sd
:
cvt
(convert - преобразовать) + s (single - число с плавающей точкой одинарной точности) + i (integer - целое число) + 2 (to - в) + s (scalar - скалярное значение) +
d(double - число с плавающей точкой двойной точности).
Пример преобразования на Linux:
global _start section .data number dq 3.4 section .text _start: movsd xmm0, [number] ; помещаем число number в xmm0 cvtsd2si rdi, xmm0 ; преобразуем число из xmm0 в целое число и помещаем в rdi mov rax, 60 syscall
В данном случае число 3.4, которое хранится в XMM0, преобразуется в целочисленное значение. И мы получим в регистре rdi число 3.
Аналогичный пример преобразования на Windows:
global _start section .data number dq 3.4 section .text _start: movsd xmm0, [rel number] ; помещаем число number в xmm0 cvtsd2si rax, xmm0 ; преобразуем число из xmm0 в целое число и помещаем в rdi ret
По умолчанию для округления чисел применяются 13-14 биты регистра состояния MXCSR:
00: округление до ближайшего
01: округление до -бесконечности
10: округление до +бесконечности
11: округление до 0 (усечение)
По умолчанию округление идет до ближайшего. Так, число 3.4 округляется до 3, а число 3.5 - до 4. Управляя этими битами, можно настроить округление при преобразовании. Например, возьмем следующую программу на Linux:
global _start section .data number dq 5.6 state dd 0 section .text _start: stmxcsr [state] ; загружаем регистр в переменную state or dword [state], 0b110000000000000 ; устанавливаем 13 и 14 биты ldmxcsr [state] ; сохраняем данные из переменной state в регистр movsd xmm0, [number] ; помещаем число number в xmm0 cvtsd2si rdi, xmm0 ; преобразуем число из xmm0 в целое число и помещаем в rdi mov rax, 60 syscall
В данном случае загружаем состояние регистра в переменную state, устанавливаем в ней 13 и 14 биты (для этого применяется инструкция orl).
Это означет, что при округлении дробная часть будет отбрасываться. Затем инструкцией ldmxcsr
сохраняем данные из переменной обратно в регистр. В итоге при преобразовании
числа 5.6 оно будет округляться до 5.
Аналогичный пример на Windows:
global _start section .data number dq 5.6 state dd 0 section .text _start: stmxcsr [rel state] ; загружаем регистр в переменную state or dword [rel state], 0b110000000000000 ; устанавливаем 13 и 14 биты ldmxcsr [rel state] ; сохраняем данные из переменной state в регистр movsd xmm0, [rel number] ; помещаем число number в xmm0 cvtsd2si rax, xmm0 ; преобразуем число из xmm0 в целое число и помещаем в rdi ret
Кроме того, есть отдельный набор инструкций, которые выполняет преобразование чисел в векторах из одного типа в другой.
cvtdq2pd
: преобразует два 32-разрядных целых числа со знаком в два 64-разрядных числа с плавающей точкой
cvtdq2pd xmmdest, xmmsrc/mem64
vcvtdq2pd
: преобразует два 32-разрядных целых числа со знаком в два 64-разрядных числа с плавающей точкой (результат в XMM)
vcvtdq2pd xmmdest, xmmsrc/mem64
либо преобразует четыре 32-разрядных целых числа со знаком в четыре 64-разрядных числа с плавающей точкой (результат в YMM)
vcvtdq2pd ymmdest, xmmsrc/mem128
cvtdq2ps
: преобразует 4 32-разрядных целых числа со знаком в 4 32-разрядных числа с плавающей точкой
cvtdq2ps xmmdest, xmmsrc/mem128
vcvtdq2ps
: преобразует 4 32-разрядных целых числа со знаком в 4 32-разрядных числа с плавающей точкой
vcvtdq2ps xmmdest, xmmsrc/mem128
Другая форма инструкции преобразует 8 32-разрядных целых числа со знаком в 8 32-разрядных чисел с плавающей точкой
vcvtdq2ps ymmdest, ymmsrc/mem256
cvtpd2dq
: преобразует два 64-разрядных числа с плавающей точкой в два 32-разрядных целых числа со знаком
cvtpd2dq xmmdest, xmmsrc/mem128
vcvtpd2dq
: преобразует два 64-разрядных числа с плавающей точкой в два 32-разрядных целых числа со знаком (результат в XMM)
vcvtpd2dq xmmdest, xmmsrc/mem128
Другая форма преобразует преобразует 4 64-разрядных числа с плавающей точкой в 4 32-разрядных целых числа со знаком
vcvtpd2dq xmmdest, ymmsrc/mem256
cvtpd2ps
: преобразует два 64-разрядных числа с плавающей точкой в два 32-разрядных числа с плавающей точкой
cvtpd2ps xmmdest, xmmsrc/mem128
vcvtpd2ps
: преобразует два 64-разрядных числа с плавающей точкой в два 32-разрядных числа с плавающей точкой
vcvtpd2ps xmmdest, xmmsrc/mem128
Другая форма преобразует преобразует 4 64-разрядных числа с плавающей точкой в 4 32-разрядных числа с плавающей точкой
vcvtpd2ps xmmdest, ymmsrc/mem256
cvtps2dq
: преобразует 4 32-разрядных числа с плавающей точкой в 4 32-разрядных целых числа со знаком
cvtps2dq xmmdest, xmmsrc/mem128
vcvtps2dq
: преобразует 4 32-разрядных числа с плавающей точкой в 4 32-разрядных целых числа со знаком
vcvtps2dq xmmdest, xmmsrc/mem128
Другая форма преобразует 8 32-разрядных числа с плавающей точкой в 8 32-разрядных целых числа со знаком
vcvtps2dq ymmdest, ymmsrc/mem256
cvtps2pd
: преобразует два 32-разрядных числа с плавающей точкой в два 64-разрядных числа с плавающей точкой
cvtps2pd xmmdest, xmmsrc/mem64
vcvtps2pd
: преобразует два 32-разрядных числа с плавающей точкой в два 64-разрядных числа с плавающей точкой
vcvtps2pd xmmdest, xmmsrc/mem64
другая форма преобразует 4 32-разрядных числа с плавающей точкой в 4 64-разрядных числа с плавающей точкой
vcvtps2pd ymmdest, xmmsrc/mem128
cvttpd2dq
: преобразует два 64-разрядных числа с плавающей точкой в два 32-разрядных целых числа со знаком
cvttpd2dq xmmdest, xmmsrc/mem128
vcvttpd2dq
: преобразует два 64-разрядных числа с плавающей точкой в два 32-разрядных целых числа со знаком
vcvttpd2dq xmmdest, xmmsrc/mem128
другая форма преобразует 4 64-разрядных числа с плавающей точкой в 4 32-разрядных целых числа со знаком
vcvttpd2dq xmmdest, ymmsrc/mem256
cvttps2dq
: преобразует 4 32-разрядных числа с плавающей точкой в 4 32-разрядных целых числа со знаком
cvttps2dq xmmdest, xmmsrc/mem128
vcvttps2dq
: преобразует 4 32-разрядных числа с плавающей точкой в 4 32-разрядных целых числа со знаком
vcvttps2dq xmmdest, xmmsrc/mem128
другая форма преобразует 8 32-разрядных чисел с плавающей точкой в 8 32-разрядных целых чисел со знаком
vcvttps2dq ymmdest, ymmsrc/mem256
Эти инструкции удобно использовать в ситуации, когда надо преобразовать вектор значений. Например, преобразуем набор 32-разрядных чисел с плавающей точкой в набор целых 32-разрядных чисел:
global _start section .data floatNums dd 4.2, 5.3, 6.4, 7.5 section .bss intNums resd 4 section .text _start: cvttps2dq xmm0, [floatNums] ; преобразуем чисел с плавающей точкой в целые числа, результат помещаем в xmm0 movaps [intNums], xmm0 ; результат помещаем в intNums mov edi, [intNums] ; edi = 4 mov rax, 60 syscall
Здесь вектор из 4 чисел с плавающей точкой преобразуем вектор целых чисел и помещаем результат в регистр xmm0. Затем результат помещаем в переменную intNums.
Аналогичная программа на Windows:
global _start section .data floatNums dd 4.2, 5.3, 6.4, 7.5 section .bss intNums resd 4 section .text _start: cvttps2dq xmm0, [rel floatNums] ; преобразуем чисел с плавающей точкой в целые числа, результат помещаем в xmm0 movaps [rel intNums], xmm0 ; результат помещаем в intNums mov eax, [rel intNums] ; edi = 4 ret