Преобразование чисел с плавающей точкой в целые числа и обратно

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

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