Вот код на FASM, использующий функции из библиотеки stdio scaf и printf Я просто ввожу и вывожу на экран число типа double, которое храниться в переменной размера qword. Этот пример работает корректно. Но есть смежные вопросы. Код (Text): format pe console entry start include 'C:\FASM_WINDOWS\include\win32a.inc' start: push x push fm call [scanf] add esp,4*2 ;----------------------------------------------------------------------- push dword[x+4] push dword[x+0] push fm call [printf] add esp,4*3 retn ;------------------------------------------------------------- x dq 0 fm db '%lf',0; lf аргумент для типа double ;-------------------------------------------------------------------- data import library msvcrt,'msvcrt.dll' import msvcrt,\ printf,'printf',\ scanf,'scanf' end data Собсвенно вопрос: Мне захотелось разобраться с форматом float. Не тут-то было. Ни переменная размера qword, ни размера dword не выводит %f правильно. Где собака зарыта? Да, и ещё вопрос. FPU работает с числами, которые помещаются в ячейки размера qword, или можно и в более большие? Аргумент scanf и printf %lf тоже увещает в себе максимум qword???
Представление (если угодно, кодирование) вещественных чисел прилично отличается от представления целых. Описано оно, например, в интеловских мануалах. Что же касается разрядности, то вещественные числа на ПК бывают 4-, 8- и 10-байтовыми.
Форматы вещественных чисел можно посмотреть тут: http://en.wikipedia.org/wiki/IEEE_754-1985 с одинарной точность, float, 32 бита, и с двойной точностью, double, 64 бит. Это хранение в памяти. Для арифметики на Интелах используюся 80-битные регистры сопроцессора. Использование спецификатора %lf для вещественных несколько неправильно. %f съедает как float, так и double. %Lf используется для long double (96 бит по умолчанию в gcc на Интеле, 128 бит например, на PowerPC).
vasia, нет. Во все функции на месте ... (ellipsis) в Си аргумент с плавающей точкой обязан передаваться как double. То есть, %f обозначает именно double и никаких float.
Хм... как раз-таки для вывода float нужно printf("%f", floatvar), а для double катит только printf("%lf", doublevar) Любые другие комбинации были опробованы и выводили полный бред (длиннющее число в две строки консоли, никак не совпадающее, с тем, чем надо), причём, понятное дело, как в фасме, так и в студии. Вопрос об использовании tbyte или tword (кстати, в чём разница между ними в фасме, ведь оба 80 бит, и один из них, помоему, объявляется как dt qword, word ? ) остаётся открытом. Привидите пример кода, где можно scanf и printf tbyteовую ячейку памяти. Сейчас попробую пооперировать сопроцессором с такими ячейками.
Код (Text): format pe console entry start include '../../include/win32a.inc' start: ;----------------------------------------------------------------------- push dword [x+4] push dword [x] push fm call [printf] add esp,4*3 retn ;------------------------------------------------------------- x dq 4f // 4.0 fm db '%f',0; ;-------------------------------------------------------------------- data import library msvcrt,'msvcrt.dll' import msvcrt,\ printf,'printf',\ scanf,'scanf' end data Выводит double. Нет разницы. tbyte и tword модификаторы поинтера, не надо их путать с директивами резервирования помяти. И модификаторы эти эквивалетны. В более старом fasm было только tword. Вот пример Код (Text): fld dword [eax] fld tbyte [eax] fld tword [eax] Последний два будут одинаковы. Код (Text): int main(int argc, char* argv[]) { double x = 1.1; long double y = 0; x = y; return 0; } Судя по дизасм-коду VC6 double и long double одинаковы.
Код (Text): void main() { double x; scanf("%lf", &x); printf("%f",x); } В printf %f корректно выводит double переменную. Но вот если в scanf передовать не "%lf", а "%f", то будет выведенна охинея.
Antolflash В printf %f корректно выводит double переменную. Но вот если в scanf передовать не "%lf", а "%f", то будет выведенна охинея. это от того, что форматная строка printf и scanf различается: http://www.cplusplus.com/reference/clibrary/cstdio/printf/ f Decimal floating point l The argument is interpreted as a long int or unsigned long int for integer specifiers (i, d, o, u, x and X), and as a wide character or wide character string for specifiers c and s. http://www.cplusplus.com/reference/clibrary/cstdio/scanf/ f Floating point l long int (for d, i and n), or unsigned long int (for o, u and x), or double (for e, f and g)
Я тут написал программу для возведения вещественного числа x в вещественную степень y. Код (Text): format pe console entry start include 'C:\FASM_WINDOWS\include\win32a.inc' start: push x push fms call [scanf] add esp,4*2 push y push fms call [scanf] add esp,4*2 ;----------------------------------------------- finit fld [one] fld [x] fld [zero] xor ecx,ecx fcomip st1 jb th je zerocase mov ecx,1 fabs th: push ecx fyl2x fld [y] fmul st0,st1 fst [ylog2x] fld [one] fxch fprem fst [osta] fld [ylog2x] fsub st0,st1 fistp [cel] f2xm1 fld [one] fadd st0,st1 fst [tpm] mov ebx,[cel] cmp ebx,0 jg multiply jl division fstp [resultat] jmp exi multiply: fld [two] fld [one] mult: dec ebx fmul st0,st1 test ebx,ebx jnz mult jmp semi division: fld [two] fld [one] divi: inc ebx fdiv st0,st1 test ebx,ebx jnz divi semi: fld [tpm] fmul st0,st1 pop ecx test ecx,ecx jz skip fld [minus] fmul st0,st1 skip: fstp [resultat] jmp exi zerocase: fild [zero] fistp[resultat] exi: ;----------------------------------------------------------------------- push dword[resultat+4] push dword[resultat+0] push fmp call [printf] add esp,4*3 jmp start retn ;------------------------------------------------------------- x dq ? y dq ? one dt 1.0 two dq 2.0 osta dq ? cel dd ? ylog2x dq ? tpm dq ? resultat dq ? fms db '%lf',0; fmp db '%lf',0x0d,0x0a,0; zero dq 0.0 minus dq -1.0 ;-------------------------------------------------------------------- data import library msvcrt,'msvcrt.dll' import msvcrt,\ printf,'printf',\ scanf,'scanf' end data Есть два вопроса. Первый - мне захотелось, чтобы в случаи целого результата вывод был целым. И второй, как добиться того, чтобы в сопроцессор можно было загружать и оперировать числами tbyte, т.е. 80 бит, а не qword 60 бит. Данный ниже код отличается от верхнего проверкой на нулевой x (работает корректно), и проверкой на целость результата и вывод в целом формате. Вот тут то и косяк, не работающее место кода помеченно. Помогите пожалуйста разобраться, почему Код (Text): format pe console entry start include 'C:\FASM_WINDOWS\include\win32a.inc' start: push x push fms call [scanf] add esp,4*2 push y push fms call [scanf] add esp,4*2 ;----------------------------------------------- finit fld [one] fld [x] fld [zero] xor ecx,ecx fcomip st1 jb th je zerocase mov ecx,1 fabs th: push ecx fyl2x fld [y] fmul st0,st1 fst [ylog2x] fld [one] fxch fprem fst [osta] fld [ylog2x] fsub st0,st1 fistp [cel] f2xm1 fld [one] fadd st0,st1 fst [tpm] mov ebx,[cel] cmp ebx,0 jg multiply jl division fst [resultat] jmp exi multiply: fld [two] fld [one] mult: dec ebx fmul st0,st1 test ebx,ebx jnz mult jmp semi division: fld [two] fld [one] divi: inc ebx fdiv st0,st1 test ebx,ebx jnz divi semi: fld [tpm] fmul st0,st1 pop ecx test ecx,ecx jz skip fld [minus] fmul st0,st1 skip: fst [resultat] jmp exi zerocase: fild [zero] fist[intres] jmp ze exi: fld[one] fxch fprem ftst jnz exii jmp pri exii: ;----------------------------------------------------------------------- push dword[resultat+4] push dword[resultat+0] push fmp call [printf] add esp,4*3 jmp start pri: fld [resultat]; КОСЯК!!!! НЕ ХОЧЕТ ЗАГРУЖАТЬ!!!!! ХОТЯ В [resultat] ЛЕЖИТ КОРРЕКТНОЕ ЧИСЛО fistp [intres] ze: push [intres] push intfmp call [printf] add esp,4*2 jmp start retn ;------------------------------------------------------------- x dq ? y dq ? one dt 1.0 two dq 2.0 osta dq ? cel dd ? ylog2x dq ? tpm dq ? resultat dq ? fms db '%lf',0; fmp db '%lf',0x0d,0x0a,0; zero dq 0.0 minus dq -1.0 intres dd ? intfmp db '%i',0x0d,0x0a,0 ;-------------------------------------------------------------------- data import library msvcrt,'msvcrt.dll' import msvcrt,\ printf,'printf',\ scanf,'scanf' end data
Antolflash С какой целью ? "Изобрести велосипед" или "потренироваться на кошках" c fpu-вычислениями ? Если "велосипед", то лучше взять готовые реализации - например, pow.asm из BCB6 или на крайняк FpuXexpY.asm из fpulib masm32. Да и здесь на форуме реализации Pow и IntPow не раз обсуждались(например, fpower, Pow, Pow, IntPower). Кстати вторая ссылка также поучительна с точки зрения ошибок начинающих fpu-прогеров (переполнение стека fpu, переполнения и замаскированные исключения и т.п.) Ну а если просто тренируешься, то беглый взгляд на твой код показывает, что ты совершенно не следишь за очисткой стека fpu, что ес-но может приводить к его переполнению - что видимо у тебя и происходит. Учти, что операция fstp освобождает регистр st0, а fst - нет. Операция fadd без операндов эквивалентна faddp st1,st0 - прибавляет st0 к st1 и удаляет st0, а операция fadd st1,st0 - не удаляет. Тоже самое относится и к fsub и fmul. Избежать лишних загрузок и освобождений регистров можно путем использования команд с операндами из памяти. Например, вместо fld [tmp] + fmulp лучше юзать просто fmul [tmp] Вторая "глупость" - загрузка констант 0 и 1 из памяти (да еще и в тормозном формате tbyte), т.к. для этого есть "мгновенные" спецоперации fldz и fld1. Третья "глупость" - использование дурных вычислений там где они не нужны, в частности изменение знака путем умножения на -1 вместо исп. "мгновенной" операции fchs; вычисление 0.5 делением 1/2 вместо использования константы fhalf dd 0.5. Здесь уместно заметить, что константы, соответсвующие целым числам и целым отрицательным степеням двойки (0.5, 0.125 и т.д.) представляются в памяти точно (без ошибок округления), поэтому для них можно юзать мин.размер операнда, в который они влезают - в частности для 2 и 0.5 вполне достаточно dd (или на крайняк dq, но уж никак не тормоза tbyte)
Писал в учебных целях, лаба такая. Спасибо, учёл. Код (Text): format pe console entry start include 'C:\FASM_WINDOWS\include\win32a.inc' start: push x push fms call [scanf] add esp,4*2 push y push fms call [scanf] add esp,4*2 ;----------------------------------------------- finit fld1 fld [x] fldz xor ecx,ecx fcomip st1 jb th je zerocase mov ecx,1 fabs th: push ecx fyl2x fld [y] fmul st0,st1 fst [ylog2x] fld1 fxch fprem fst [osta] fld [ylog2x] fsub st0,st1 fistp [cel] f2xm1 fld1 fadd st0,st1 fst [tpm] mov ebx,[cel] cmp ebx,0 jg multiply jl division fst [resultat] jmp exi multiply: fld [two] fld1 mult: dec ebx fmul st0,st1 test ebx,ebx jnz mult jmp semi division: fld [half] fld1 divi: inc ebx fmul st0,st1 test ebx,ebx jnz divi semi: fld [tpm] fmul st0,st1 pop ecx test ecx,ecx jz skip fchs skip: fst [resultat] jmp exi zerocase: fldz fist[intres] jmp ze exi: fld1 fxch fprem fxch fstp [musor] fldz fcomip st1 jnz exii jmp pri exii: ;----------------------------------------------------------------------- push dword[resultat+4] push dword[resultat+0] push fmp call [printf] add esp,4*3 jmp start pri: fistp[musor] fld [resultat] fistp [intres] ze: push [intres] push intfmp call [printf] add esp,4*2 jmp start retn ;------------------------------------------------------------- x dq ? y dq ? half dd 0.5 two dd 2.0 osta dq ? cel dd ? ylog2x dq ? tpm dq ? resultat dq ? fms db '%lf',0; fmp db '%f',0x0d,0x0a,0; intres dd ? intfmp db '%i',0x0d,0x0a,0 musor dq ? ;-------------------------------------------------------------------- data import library msvcrt,'msvcrt.dll' import msvcrt,\ printf,'printf',\ scanf,'scanf' end data Только вот вопрос с использование TBYTE открыт. Команда fst [tbyte_var] не работает
Вопрос решён Команда fst копирует значение из вершины стека сопроцессора в операнд-адресат, которым может быть 32-битное или 64-битное расположение в памяти или другой регистр FPU. fstp совершает ту же операцию, но далее выталкивает значение из стека, освобождая ST(0). fstp может сохранять ещё и 80-битное значение в память.
На вскидку видно, что ни фига ты не учел - добавил одну\другую очистку, ошибка пропала и все. По правилам программирования fpu после выполнения вычислений стек fpu должен быть полностью очищен, иначе это может аукнуться в дальнейшем. А ты вместо нормальной работы со стеком полагаешься на грубую силу - тормозную операцию finit, которую обычно в программе вызывают всего один раз, а не в начале каждого цикла. В общем, "садись, два !"
Тебе уже объяснили, что scanf и printf работают только с double и соотв-но Lf это тоже самое, что просто f
А кто работает с long double??? Самому писать ввод-вывод? Наверно придётся разбираться с портами ввода-вывода.
Antolflash Шутка удалась. ReadConsole/WriteConsole + функции перевода числа в строку и строки в число.
Хм... Я было начал этим заниматься, но бросил изучение WIN API. Так что получается, в обычном C есть тип long double, но нет стандартных библиотечных средств его ввода-вывода?