Подтвердить что ты не робот

Как: pow (real, real) в x86

Я ищу реализацию pow(real, real) в сборке x86. Также я хотел бы понять, как работает алгоритм.

4b9b3361

Ответ 1

Просто вычислите его как 2^(y*log2(x)).

Существует команда x86 FYL2X для вычисления y * log2 (x) и команды x86 F2XM1 для выполнения возведения в степень. F2XM1 требует аргумента в диапазоне [-1,1], поэтому вам нужно будет добавить некоторый код между ними, чтобы извлечь целую часть и оставшуюся часть, увеличив остаток, используйте FSCALE для масштабирования результата с соответствующей мощностью 2.

Ответ 2

ОК, я реализовал power(double a, double b, double * result); в x86 так же, как вы рекомендовали.

Код: http://pastebin.com/VWfE9CZT

%define a               QWORD [ebp+8]
%define b               QWORD [ebp+16]
%define result          DWORD [ebp+24]
%define ctrlWord            WORD [ebp-2]
%define tmp             DWORD [ebp-6]

segment .text
    global power

power:
    push ebp
    mov ebp, esp
    sub esp, 6
    push ebx

    fstcw ctrlWord
    or ctrlWord, 110000000000b
    fldcw ctrlWord

    fld b
    fld a
    fyl2x

    fist tmp

    fild tmp
    fsub
    f2xm1
    fld1
    fadd
    fild tmp
    fxch
    fscale

    mov ebx, result
    fst QWORD [ebx]

    pop ebx
    mov esp, ebp
    pop ebp
    ret

Ответ 3

Здесь моя функция с использованием основного алгоритма "Свин". Я завернул его в __fastcall & __declspec (голый) декорации и добавил код, чтобы убедиться, что base/x является положительным. Если x отрицателен, FPU полностью потерпит неудачу. Вам нужно проверить бит знака 'x', а также учесть нечетный/четный бит 'y' и применить знак после его завершения! Дай мне знать, что ты думаешь для любого случайного читателя. Если возможно, ищите еще лучшие версии с кодом x87 FPU. Он компилируется/работает с Microsoft VC++ 2005, что я всегда придерживаюсь по разным причинам.

Совместимость v. ANSI pow (x, y): очень хорошо! Более быстрые, предсказуемые результаты, отрицательные значения обрабатываются, просто нет ошибок обратной связи для неверного ввода. Но, если вы знаете, что 'y' всегда может быть INT/LONG, НЕ используйте эту версию; Я опубликовал версию Agner Fog с некоторыми изменениями, которые позволяют избежать очень медленного FSCALE, поищите его в моем профиле! Это самый быстрый способ для x87/FPU в этих ограниченных условиях!

extern double __fastcall fs_Power(double x, double y);

// Main Source: The Svin
// pow(x,y) is equivalent to exp(y * ln(x))
// Version: 1.00

__declspec(naked) double __fastcall fs_Power(double x, double y) { __asm {
    LEA   EAX, [ESP+12]         ;// Save 'y' index in EAX
    FLD   QWORD PTR [EAX]       ;// Load 'y' (exponent) (works positive OR negative!)
    FIST  DWORD PTR [EAX]       ;// Round 'y' back to INT form to test for odd/even bit
    MOVZX EAX, WORD PTR [EAX-1] ;// Get x left sign bit AND y right odd/even bit!
    FLD   QWORD PTR [ESP+4]     ;// Load 'x' (base) (make positive next!)
    FABS            ;// 'x' MUST be positive, BUT check sign/odd bits pre-exit!
    AND   AX, 0180h ;// AND off all bits except right 'y' odd bit AND left 'x' sign bit!
    FYL2X       ;// 'y' * log2 'x' - (ST(0) = ST(1) * log2 ST(0)), pop
    FLD1        ;// Load 1.0f: 2 uses, mantissa extract, add 1.0 back post-F2XM1
    FLD   ST(1) ;// Duplicate current result
    FPREM1      ;// Extract mantissa via partial ST0/ST1 remainder with 80387+ IEEE cmd
    F2XM1       ;// Compute (2 ^ ST(0) - 1)
    FADDP ST(1), ST ;// ADD 1.0f back! We want (2 ^ X), NOT (2 ^ X - 1)!
    FSCALE      ;// ST(0) = ST(0) * 2 ^ ST(1) (Scale by factor of 2)
    FFREE ST(1) ;// Maintain FPU stack balance
;// Final task, make result negative if needed!
    CMP   AX, 0180h    ;// Combo-test: Is 'y' odd bit AND 'x' sign bit set?
    JNE   EXIT_RETURN  ;// If positive, exit; if not, add '-' sign!
        FCHS           ;// 'x' is negative, 'y' is ~odd, final result = negative! :)
EXIT_RETURN:
;// For __fastcall/__declspec(naked), gotta clean stack here (2 x 8-byte doubles)!
    RET   16     ;// Return & pop 16 bytes off stack
}}

Хорошо, чтобы завершить этот эксперимент, я запустил бенчмарк-тест, используя инструкцию RDTSC по меткам времени/счетчику тактов. Я последовал совету также установить высокий приоритет процесса с помощью "SetPriorityClass (GetCurrentProcess(), HIGH_PRIORITY_CLASS);" и я закрыл все остальные приложения.

Результаты: наша ретро-математическая функция x87 FPU "fs_Power (x, y)" работает на 50-60% быстрее, чем версия MSCRT2005 pow (x, y), которая использует довольно длинную ветвь кода SSE, помеченную '_pow_pentium4:', если она обнаруживает 64-битный> Pentium4+ CPU. Так яааааа !! :-)

Примечания: (1) CRT pow() имеет ветвь инициализации ~ 33 микросекунды, которая, по-видимому, показывает нам 46 000 в этом тесте. Он работает в среднем после 1200–3000 циклов. Наша созданная вручную красота x87 FPU работает без перебоев при первом вызове!

(2) Хотя CRT pow() проиграл каждый тест, он выиграл в ОДНОЙ области: если вы ввели дикие, огромные значения вне диапазона/переполнения, он быстро вернул ошибку. Поскольку большинству приложений не требуется проверка ошибок при обычном/обычном использовании, это не имеет значения.

https://i.postimg.cc/QNbB7ZVz/FPUv-SSEMath-Power-Proc-Test.png

2-й тест (мне пришлось запустить его снова, чтобы скопировать/вставить текст после привязки изображения):

 x86 fs_Power(2, 32): CPU Cycles (RDTSC): 1248
MSCRT SSE pow(2, 32): CPU Cycles (RDTSC): 50112

 x86 fs_Power(-5, 256): CPU Cycles (RDTSC): 1120
MSCRT SSE pow(-5, 256): CPU Cycles (RDTSC): 2560

 x86 fs_Power(-35, 24): CPU Cycles (RDTSC): 1120
MSCRT SSE pow(-35, 24): CPU Cycles (RDTSC): 2528

 x86 fs_Power(64, -9): CPU Cycles (RDTSC): 1120
MSCRT SSE pow(64, -9): CPU Cycles (RDTSC): 1280

 x86 fs_Power(-45.5, 7): CPU Cycles (RDTSC): 1312
MSCRT SSE pow(-45.5, 7): CPU Cycles (RDTSC): 1632

 x86 fs_Power(72, -16): CPU Cycles (RDTSC): 1120
MSCRT SSE pow(72, -16): CPU Cycles (RDTSC): 1632

 x86 fs_Power(7, 127): CPU Cycles (RDTSC): 1056
MSCRT SSE pow(7, 127): CPU Cycles (RDTSC): 2016

 x86 fs_Power(6, 38): CPU Cycles (RDTSC): 1024
MSCRT SSE pow(6, 38): CPU Cycles (RDTSC): 2048

 x86 fs_Power(9, 200): CPU Cycles (RDTSC): 1152
MSCRT SSE pow(9, 200): CPU Cycles (RDTSC): 7168

 x86 fs_Power(3, 100): CPU Cycles (RDTSC): 1984
MSCRT SSE pow(3, 100): CPU Cycles (RDTSC): 2784

Какие-нибудь реальные приложения? ДА! Pow (x, y) интенсивно используется для кодирования/декодирования формата CD WAVE в OGG и наоборот! Когда вы кодируете полные 60 минут данных WAVE, вы сэкономите время, и это будет значительным! Многие математические функции используются в OGG/libvorbis, например, acos(), cos(), sin(), atan(), sqrt(), ldexp() (очень важно) и т.д. Итак, такие хорошо настроенные версии, которые не беспокоить/проверять ошибки, может сэкономить много времени !!

Мой эксперимент - результат создания OGG-декодера для системы установки NSIS, который привел меня к замене всех функций библиотеки Math "C", которые нужны алгоритму, на то, что вы видите выше. Ну, ПОЧТИ, мне нужен acos() в x86, но я ВСЕ ЕЩЕ не могу найти ничего для этого...

С уважением, и надеюсь, что это полезно для всех, кто любит повозиться!