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