Вот написал процедуру для конвертации из single precision в ASCII без использования FPU. Как ещё можно оптимизировать этот код? Код (Text): procedure float2str(x: single; s: PChar);stdcall;assembler; asm push ebx push edi mov edi,[s] mov edx,[x] mov ecx,edx shr ecx,23 mov byte[edi],'-' movzx eax,ch mov byte[edi+eax],'0' lea edi,[edi+eax+1] and edx,$7FFFFF or edx,$800000 //мантисса shl edx,8 movzx ecx,cl xor eax,eax sub ecx,126 //экспонента jl @noint shld eax,edx,cl //целая часть shl edx,cl //дробная часть push edx push esi mov ebx,429496730 mov esi,esp @2:mov ecx,eax mul ebx dec esi sub ecx,edx lea eax,[edx*8+edx-'0'] sub ecx,eax mov eax,edx mov [esi],cl test edx,edx jne @2 movups xmm0,[esi] movups [edi-1],xmm0 lea edi,[edi+esp-1] sub edi,esi pop esi pop edx xor ecx,ecx @noint: test edx,edx je @nofloat neg ecx shr edx,cl mov ebx,10 mov eax,edx mov byte[edi],'.' @1:mul ebx inc edi add edx,'0' test eax,eax mov [edi],dl jne @1 inc edi @nofloat: stosb //завершающий ноль pop edi pop ebx end;
murder А как же денормализованные числа? Для них добавлять единицу совсем не нужно. на мой взгляд это очень сомнительная конструкция, будет ли она работать при cl>31?! А как же всякие исключения? Если не использовать FPU, то какой смысл тянуть SSE из-за двух команд? А всякие NAN и INF данной процедурой в принципе не выводятся?!
1) Не понял. На сколько я знаю все числа в формате с одинарной точностью должны быть нормализованы. Поясни. 2) shld при cl>31 действительно не работает (воспринимаются только младшие 5 бит), но это не важно так как числа, большие чем 2^31 всё равно не могут быть нормально представлены в этом формате (по крайней мере не все). Да и к тому же тогда придётся реализовывать конвертацию 64 битных чисел, а это уже не так красиво. 3) Про исключения тоже не совсем понятно. Там ведь в стек помещается не более 10 байт - это не так уж и много. 4) movups мне и самому не нравится, но смотрится очень уж изящно. В общем-то проблема в самом алгоритме, а не в реализации. Было-бы не плохо если бы число конвертировалось в прямом порядке, а не в обратном. В мануалах от AMD берётся обратное значение числа, а затем конвертируется при помощи умножения на 10 (также как дробная часть в этой процедуре). Попробую прикрутить этот метод. 5) Про NAN и INF я просто забыл. Обязательно добавлю. Мне кажется можно избавиться от умножения на 10, но ничего путного придумать не могу.
Про денормализованные числа прочитал. Теперь всё понятно. Не знаю стоит ли усложнять программу ради чисел с порядком меньше -126, это ведь практически ноль. В общем если получится сделать это изящно, то добавлю поддержку денормализованых.
Добавил поддержку NaN и INF. вот так это выглядит Код (Text): mov byte[edi],'-' movzx eax,ch add edi,eax mov [edi],'FNI' mov ebx,'NaNs' and edx,$7FFFFF cmove ebx,[edi] mov eax,edx and eax,$00400000 mov [edi+4],al shr eax,21 sub ebx,eax mov [edi],ebx cmp cl,255 je @exit Обнаружилась ещё одна особенность Код (Text): shr edx,cl Так у меня выделяется дробная часть. Тут очевидно, что теряются биты и опять таки при cl>31 будет неправильно работать. Вопрос такой: как узнать количество лидирующих нолей для десятичной записи числа x*2^y (y<0). И аналогично - количество завершающих нулей для x*2^y (y>0). Видимо нужно привести число к виду x*10^y.
Интересная закономерность: возьмём число 2^x И применим формулу: y=trunc((abs(x)-1-trunc((abs(x)-1)/10))/3)+1 Если x>=0, то y - это длина десятичного числа Иначе это количество лидирующих нулей