а! насчот этово у MSVS опция есть /QIfist (интеловский тож ее понимет). Или была по крайней мере в VC6++ (а тож мсявки совсем испортеле плюсы)... /add кстати, а, изначальный алгос Код (Text): int floor_i32(float x) { int i=x; if(i>x) i--; return i; } достаточно корректен? каким методы для его теста можно использовать? а то intel и gcc наиболее круто уложились в маш код intel /Ox /arch:SSE Код (Text): _floor_i32 PROC NEAR ; parameter 1: 4 + esp .B1.1: movss xmm3, DWORD PTR [esp+4] cvtss2si eax, xmm3 lea edx, DWORD PTR [eax-1] cvtsi2ss xmm4, eax comiss xmm3, xmm4 cmovb eax, edx ret GCC 3.4.6 (-O2 -Os -msse -mregparm=3 -masm=intel -momit-leaf-frame-pointer ) Код (Text): _floor_i32: movss xmm1, DWORD PTR [esp+4] cvttss2si eax, xmm1 cvtsi2ss xmm0, eax lea edx, [eax-1] ucomiss xmm0, xmm1 cmova eax, edx ret MS 32-bit C/C++ Compiler Version 14.00.50727.42 /Ox /arch:SSE Вобщем то непонятно что и за'оптимайзил. Так будет правельней если да и код не слишком то видоизменился при этом, с опциями /Ox /G6 /QIfist Код (Text): tv130 = -8 ; size = 8 _i$ = 8 ; size = 4 _x$ = 8 ; size = 4 _floor_i32 PROC sub esp, 8 ; Line 3 fld DWORD PTR _x$[esp+4] fld ST(0) fistp QWORD PTR tv130[esp+8] mov ecx, DWORD PTR tv130[esp+8] mov DWORD PTR _i$[esp+4], ecx ; Line 4 fild DWORD PTR _i$[esp+4] fcompp fnstsw ax test ah, 65 ; 00000041H jne SHORT $LN4@floor_i32 sub ecx, 1 $LN4@floor_i32: ; Line 5 mov eax, ecx ; Line 6 add esp, 8 ret 0 Кстати а 14-ая версия компилера ет какой MSVS? а то код как я поглядел практически один в один как у VC 6 :-D, с той лишь разницей что c /QIfist я получил варненг да уш...
Значит по порядку. Вариант Black_mirror страдает той же бедой - приведение к int, что без SSE очень долго. 14-версия MSVS - это MSVS2005 Опция /QIfist есть и в 2008, так что её не выкинули. Но влияет на весь код - что не хорошо. Алгоритм изначальный корректен - тестировать можно логически При приведении к int округление происходит к нулю, а нам надо к минус бесконечности. Соответственно для положительных чисел результат будет одинаковый, а для отрицательных округление будет вверх. А нам надо вниз, поэтому вычитаем единицу если округлённый результат больше исходного (для всех отрицательных вычитать нельзя, т.к. целые числа сами к себе и округлятся; если вычесть - будет ошибка). Протестировал на AMD варианты с float и double. Не знаю почему (на Intel такого нет), но вариант с float рвёт вариант с double на ~15тактов. Интересно. bugaga а каким gcc ты проверяеш? в смысле есть нормальный порт под Win32, или ты под Nix компилируеш? Кстати Intel'овский компилятор меня расстроил - не понимает 3DNow! даже во встроенном асме -= добавлено =- Сейчас вот проверил. Вариант с QIfist выполняется примерно так же (медленнее на 1-2 такта) чем вариант с float и сложением с magic константой.
cppasm Из портов пакет Mingw32 & Msys, сравнительно качественный (вроде даж 64-bit версия наличествует), хотя я Cygwin юзаю, *nix framework под винду. С cygwin'ом идет GCC 3.4.4, но тут мне дистр SlackWare11 сгодился, так что апгрейдил его до версии 3.4.6 (с прицелом на кросс-компиляние вобщемто).
Вот кстати нашёл интересную игрушку http://www.codeplay.com/vectorc/index_pc.html Демо, но можно зарегистрировать (в общем кто ищет - тот найдёт). Оптимизирующий компилятор с поддержкой SSE, SSE2, 3DNow! Особенно мне понравилась интерактивная оболочка (по функционалу, по виду почти блокнот). В левом окне пишеш исходник или открываеш готовый, выбираеш для какого процессора компилировать и с какой степенью оптимизации, жмёш Compile и в правом окне получаеш исходник на ASM. Можно объектники получать - форматов много поддерживает. Можно компилировать отдельно по функциям а не весь исходник. Выдаёт различные советы по оптимизации - он кстати уверяет что float быстрее double будет Жалко что забросили его... Интересный был по-моему продукт.
В общем я нашёл вариант который меня устраивает Вот на asm с использованием 3dNow!: Код (Text): int static __inline f2int_3dnow(float f32) { int i32; __asm{ femms movd mm0,[f32] pf2id mm1,mm0 pi2fd mm2,mm1 pfcmpgt mm2,mm0 paddd mm1,mm2 movd [i32],mm1 femms } return i32; } А вот на Си, рабочий при условии что для float используется формат IEEE574 (компиляция с /QIfist): Код (Text): int static __inline f2int(float f32) { int i32; i32 = f32; f32 -= i32; if(*(int *)&f32 & 0x80000000) i32--; return i32; } Получено из начального варианта таким образом: if(i32 > f32) i32--; --> if(f32 - i32<0) i32--; Ну в принципе это всё, за исключением того что с нулём я сравниваю не во float (это дольше), а проверкой старшего бита числа - если 1 значит отрицательное. У предыдущих вариантов выиграло 8-10 тактов. Как ни странно выиграло у 3DNow! 2 такта, хотя может это погрешности замеров. Правда тут особо распаралеливать нечего. Признаюсь сочинил я это не совсем сам Идея взята из Microsoft'овской функции _ftol2_sse2 из RTL VS2005. Если бы они идею не испортили, не пришлось бы вообще сочинять велосипед. А так ихняя функция не инлайнится, и плюс к этому напихано опять же манипуляций с FPU Control Word. У меня фантазия иссякла, быстрее что-то ничего не получается PS: ёлки, я MS crt в отладчике изучал, а оказывается есть исходники в поставке
cppasm В таком виде писать сравнение нельзя, потому что 0x80000000 - это тоже ноль, только со знаком минус, понятно что ситуация редкая, но я бы для проверки что float<0 использовал бы if(*(unsigned int *)&f32 > 0x80000000U).
Код (Text): fld [f32];<-80000000h=0.0 fist [i32];->0 fisub [i32];=0 fstp [f32];->80000000h=0.0 test [f32],80000000h;=80000000h jz .m dec [i32];->FFFFFFFFh=-1 .m: Вообще если трактовать +0 как "может быть 0 или чуть больше", а -0 как "немного меньше 0", то -1 можно считать правильным результатом. Было также обнаружено, что +0 + -0 = -0 + +0 = +0 - -0 = +0 * +0 = -0 * -0 = +0, а -0 - +0 = +0 * -0 = -0 * +0 = -0
Да, насчёт -0 я в курсе Я просто решил пренебречь. Хотя в принципе можно и заменить на (*(unsigned *)&f32 > 0x80000000U) - машинный код получается почти идентичный. Просто вместо Код (Text): test reg32,80000000h jz @skip_dec получается Код (Text): cmp reg32,80000000h jbe @skip_dec