Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 [ 76 ] 77 78 79 80 81 82 83 84 85 86 87 88

DIVIDE FIXED

ENDP

Этот код даст тот же результат, что и инструкция DIV для О <= х < 232, О < d < 232

Следует обратить внимание на то, что строка JC DOVERFL и ее цель не нужны, eci„ есть уверенность, что х < OFFFFFFFFH.

Если степени 2 случаются так редко, что не стоит делать специальную оптимизации для них, можно убрать переход на DSHIFT и делать вместо него умножение с CORRECTION = О для случая А.

Если делитель меняется так часто, что процедура SET DIVISOR нуждается в оптимизации, то можно заменить инструкцию BSR кодом, который приведен в п. 6.26.15 для процессоров Р1 и РММХ.

6.27.2.3. Деление чисел с плавающей запятой (все процессоры). Деление чисел с пла-вающей запятой занимает 38 или 39 тактов при самой высокой точности. Можно сэкономить некоторое время, указав более низкую точность в контрольном слове (на Pi и РММХ только FDIV и FIDIV выполняются несколько быстрее при низкой точности; на РРго, Р2 и РЗ это также относится к FSQRT). Выполнение других инструкций ускорить выполнение кода этим способом невозможно.

6.27.2.4. Параллельное деление (Р1 и РММХ). На Р1 и РММХ можно производить деление числа с плавающей запятой и целочисленное деление параллельно. На РРго, Р2 и РЗ это не возможно, потому что целочисленное деление и деление чисел с плавающей запятой используют один и тот же механизм.

Пример: А = А1 / А2; В = В1 / В2

FILD

FILD

FDIV

FISTP

[Bl] [B2]

EAX, [All EBX, [A21

[A], EAX

Следует убедиться, что в контрольном слове FPU установлен желаемый метод округления.

6.27.2.5. Использование обратных инструкций для быстрого деления (РЗ). На РЗ возможно использование быстрых обратных инструкций RCPSS или PCPPS с делителем, а затем умножение результата на делимое. Правда, точность будет всего лишь 12 бит-Однако ее можно повысить до 23-бит, используя метод Ньютона-Рафсона, описанный в сопроводительной заметке АР-803 от Intel: хО = RCPSS(d)

х1 = хО * (2 - d * хО) = 2*х0 - d * хО * хО,

где хО - это первое приближение к обратному от делителя d, а х1 - лучшее прибли-jeHHC. Нужно использовать эту формулу перед умножение на делимое:

MOVAPS XMMl, [DIVISORSl ; загружаем делители

RCPPS XMMO, XMMl ; приближенное обратное число

MULPS XMMl, XMMO ; формула Ньютона-Рафсона

MULPS XMMl, XMMO

ADDPS XMMO, XMMO

SUBPS XMMO, XMMl

MULPS XMMO, [DIVIDENDS! ; результаты в XMMO

Это позволяет сделать 4 деления за 18 тактов с точностью 23 бита. Повысить точность, повторяя формулу Ньютона-Рафсона можно, но не очень выгодно.

Если использовать этот метод для целочисленного деления, тогда нужно проверять результат на ошибки округления. Следующий код делает четыре деления с усечением на упакованных целых числах размером в слово за, примерно, 42 такта. Это дает точные результаты для О <= делимое < 7FFFFH и О < делитель lt;= 7FFFFH:

; загружаем четыре делителя

; загружаем четыре делимых

; распаковываем делители в DWORDh

; конвертируем делители в плавающие

; числа, (два верхних из них)

; конвертируем нижние два операнда

; распаковываем делимые в DWORDh

MOVQ MMl, [DIVISORSl MOVQ MM2, [DIVIDENDS] PUNPCKHWD MM4, MMl PSRAD MM4, 16 PUNPCKLWD MM3, MMl PSRAD MM3, 16 CVTPI2PS XMMl, MM4

MOVLHPS XMMl, XMMl CVTPI2PS XMMl, MM3 PUNPCKHWD MM4, MM2 PSRAD MM4, 16 PUNPCKLWD MM3, MM2 PSRAD MM3, 16

CVTPI2PS XMM2, MM4 ; конвертируем делимые d плавающие числа

; (верхние два операнда)

MOVLHPS ХММ2, ХММ2

CVTPI2PS ХММ2, ММЗ ; конвертируем два нижних операнда

RCPPS ХММО, ХММ1 ; приближенное обратное число делителей

MULPS XMMl, ХММО ; улучшаем точность методом Ньютона-Рафсона

PCMPEQW ММ4, ММ4 ; создаем четыре целочисленных единицы за раз

PSRLW ММ4, 15

MULPS XMMl, ХММО

ADDPS ХММО, ХММО

SUBPS ХММО, ХММ1 ; обратные делители с точностью в 23 бита

MULPS ХММО, ХММ2 ; умножаем на делимые

CVTTPS2PI ММО, ХММО ; усекаем нижние два результата

MOVHLPS ХММО, ХММО



CVTTPS2PI ммз, хммо PACKSSDW ММО, ММЗ MOVQ ММЗ, ММ1 PMULLW ММЗ, ММО PADDSW ММО, ММ4

PADDSW ММЗ, ММ1

PCMPGTW ММЗ, ММ2

PADDSW ММО, ММЗ

MOVQ [QUOTIENTS], ММО

усекаем верхние два результата

упаковываем четыре результата в MMj

умножаем результаты на делители...

чтобы выявить ошибки округления

добавляем 1, чтобы скомпенсировать

последнее вычитание

добавляем делитель, он должен быть

больше делимого

проверяем, не слишком ли мал

вычитаем 1, если это не так

сохраняем четыре результата

Этот код проверяет, не слишком ли мал результат и делает соответствующую коррекцию. Не нужно проверять, если результат слишком велик.

6.27.2.6. Избегание делений (все процессоры). Очевидно, что необходимо минимизировать число делений в алгоритмах. Деления с плавающей запятой на константу или повторяющиеся деления на одно и то же значения следует делать через умножения на обратное число. Но есть много других ситуаций, когда можно снизить число делений. Например: if (А/В >с) можно переписать как if (А > В*С), если В положительно, и как обратное сравнение, если В отрицательны.

А/В + C/D можно переписать как (A*D + С*В) / (B*D)

Если используется целочисленное деление, стоит остерегаться того, что погрешности округления могут стать другими после переписывания формул.

6.27.3. Освобоадение регистров FPU (все процессоры)

Необходимо освобождать все использованные регистры FPU до выхода из подпрограммы, не считая регистра, используемого для возвращения результата.

Самый быстрый способ освободить один регистр - это FSTP ST. Самый быстрый способ освбодить два регистра на Р1 и РММХ - это FCOMPP, на процессорах РРго, Р2 и РЗ можно использовать как FCOMPP, так и FSTP ST одновременно.

Не рекомендуется использовать FFREE.

6.27.4. Переход от инструкций FPU к ММХ и обратно

(РММХ, Р2 и РЗ)

Необходимо вызывать инструкцию EMMS после инструкции ММХ, за которой может последовать код с инструкциями FPU.

На РММХ переключение между инструкциями FPU и ММХ вызывает высокие потб ри производительности. Выполнение первой инструкции FPU после EMMS занимает

примерно на 58 тактов больше, а первой инструкции ММХ после инструкции FP на 38 тактов больше.

На Р2 и РЗ подобных потерь нет. Задержку после EMMS можно скрыть, поме( целочисленные инструкции между EMMS и первой инструкции FPU.

6.27.5. Преобразование чисел с плавающей запятой в цел) (все процессоры)

Все подобные преобразования должны осуществляться посредством памяти:

FISTP DWORD PTR [TEMP] MOV EAX, [TEMP]

Ha PPro, P2 и РЗ этот код может вызвать потерит из-за попытки считать из [TEMP того, как закончена запись, потому что инструкция FIST медленная (гл. 6.17). WAIT поможет (п. 6.26.6). Рекомендуется поместить другие инструкции между запи в [TE1V1P] и чтением из него, что бы избежать этих потерь. Это относится ко всем пру рам, которые будут здесь рассмотрены.

Спецификации языков С и С++ требует, чтобы конверсия чисел с плавающей зага в целые числа осуществлялась с помощью усечения, а не округления. Метод, используй большинством библиотек С, - изменение контрольного слова FPU, чтобы указать инст] ции FISTP на усечение, и возврат контрольного слова в прежнее состояние после ее вы1 нения. Это метод очень медленный на всех процессорах. На РРго, Р2 и РЗ контроль слово FPU не может быть переименовано, поэтому все последующие инструкции с i вающей запятой будут ждать, пока инструкция FLDCW не будет выведена из обращена

Если нужно осуществить конверсию числа с плавающей запятой в С или С++, сле подумать о том, не лучше ли использовать округление вместо усечения. Если станд. ная библиотека не поддерживает быструю функцию округления, тогда можно сдех свою собственнук! реализацию.

Если нужно усечение внутри цикла, можно изменить контрольное слово за его пре лами, если инструкции с плавающей запятой внутри цикла могут корректно работ с данным режимом конвериторования.

Можно использовать различные способы для того, чтобы усечь аргументы без из нения контрольного слова. В данных примерах предполагается, что контрольное сл установлено по умолчанию, т. е. округление к ближайшему.



Ассемблер в задачах защи™ информац,

6.27.5.1. Округление к ближайшему.

; extern "С" int round (double х);

round PROC NEAR

PUBLIC round

FLD QWORD "PTR [ESP + 4]

FISTP DWORD PTR (ESP+4] MOV EAX, DWORD PTR [ESP+

round ENDP Усечение к нулю

; extern "С" int truncate (flouble truncate PROC NEAR PUBLIC truncate

QWORD PTR

[ESP+41

ESP, 12

FIST

DWORD PTR

[ESP]

DWORD PTR

[ESP+41

FISUB

DWORD PTR

[ESP]

FSTP

DWORD PTR

[ESP+8]

TEST

ECX, ECX

SHORT NEGATIVE

EDX, 7FFFFFFFH

EAX, 0

NEGATIVE:

ECX, ECX

TEST

EDX, EDX

SETG

EAX, ECX

; память для лока-льных переменных ; округленное значение ; значение с плавающей запятой ; вычитаем округленное значение ; разность

; округленное значение ; значение с плавающей запятой ; разность (с плавающей запятой) ; тестируем знак х

г устанавливаем флаг переноса, если ; разность меньше -О ; вычитаем 1, если x-round(x) < -С

; extern "С" int ifloor ifloor PROC NEAR PUBLIC ifloor

FIST

FISUB

FSTP

QWORD PTR ESP, 8 DWORD PTR DWORD PTR DWORD PTR EAX

[ESP+4]

[ESP] [ESP] [ESP+41

; 1, если разность > О

; добавляем 1, если x-round(x

) >

truncate ENDP

6.27.5.2. Усечение к минус бесконечности.

double X)

память для локальных перемен округленное значение вычитаем округленное значени разность

округленное значение

рлава 6. Оптимизация для процессоров семейства Pentium 471

POP ADD

SBB RET ifloor ENDP

EDX, 7FFFFFFFH EAX, 0

разность (с плавающей запятой) устанавливаем флаг переноса, если разность меньше -О вычитаем 1, если x-round(x) < -О

Эти процедуры работают для -231 < х < 231-1. Они не проверяют на переполнение или NAN.

У РЗ есть инструкции для усечения чисел с плавающей запятой одинарной точности: CVTTSS2S1 and CVTTPS2PI. Эти инструкций очень полезны, если одинарная точность удовлетворяет, но если конвертируется число с более высокой точностью в число с одинарной можно столкнуться с тем, что оно округлится вверх к большему.

6.27.5.3. Альтернатива инструкции FISTP (PI и РММХ). Конвертирование числа с плавающей запятой в целое обычно осуществляется следующим образом:

FISTP DWORD PTR [TEMP] MOV EAX, [TEMP]

Альтернативный метод заключает в:

.DATA

ALIGN

TEMP

MAGIC

. CODE

FADD

FSTP

59C00(

FPU-представпение 251 + 252

[MAGIC]

QWORD PTR [TEMP] EAX, DWORD PTR [TEMP]

При добавлении волшебного числа 251+252 существует такой эффект, что любое целое число в пределах между -231 и +231 будет выравнено в ниж- ix 32-х битах, когда сохраняется как число с плавающей запятой двойной точности. Результат будет такой же, как если бы оно было получено с помощью инструкции F1STP со всеми методами Офуления, кроме усечения к нулю. Результат будет отличаться от FISTP, если в консольном слове задано усечение или в случае переполнения. Здесь может потребоваться инструкция WAIT для совместимости со старым 80287 сопроцессором (пункт 6.26.6)

Этот метод не быстрее использования F1STP, но он дает большую гибкость на Р1 " РММХ, потому что между инструкциями FADD и FSTP есть 3 такта, которые можно заполнить другими операциями. Можно, например, умножить или разделить число

степень 2 в той же операции, сделав обратные преобразования по отношению к маги-"скому числу. Также можно добавить константу, добавив ее к магическому числу, кото-Рое в этом случае будет иметь двойную точность.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 [ 76 ] 77 78 79 80 81 82 83 84 85 86 87 88