Представление вещественных чисел

Тема в разделе "WASM.BEGINNERS", создана пользователем Antolflash, 9 апр 2009.

  1. Antolflash

    Antolflash New Member

    Публикаций:
    0
    Вот код на FASM, использующий функции из библиотеки stdio scaf и printf
    Я просто ввожу и вывожу на экран число типа double, которое храниться в переменной размера qword. Этот пример работает корректно. Но есть смежные вопросы.


    Код (Text):
    1. format  pe console
    2.         entry   start
    3.         include 'C:\FASM_WINDOWS\include\win32a.inc'
    4.  
    5. start:
    6.  
    7.       push x
    8.       push fm
    9.       call [scanf]
    10.       add esp,4*2
    11.  
    12. ;-----------------------------------------------------------------------
    13.       push dword[x+4]
    14.       push dword[x+0]
    15.       push fm
    16.       call [printf]
    17.       add esp,4*3
    18.  
    19.         retn
    20. ;-------------------------------------------------------------
    21. x dq 0
    22. fm db '%lf',0; lf аргумент для типа double
    23.  
    24.  
    25. ;--------------------------------------------------------------------
    26. data    import
    27.         library msvcrt,'msvcrt.dll'
    28.         import  msvcrt,\
    29.                 printf,'printf',\
    30.                 scanf,'scanf'
    31.         end     data
    Собсвенно вопрос:
    Мне захотелось разобраться с форматом float. Не тут-то было. Ни переменная размера qword, ни размера dword не выводит %f правильно. Где собака зарыта?

    Да, и ещё вопрос. FPU работает с числами, которые помещаются в ячейки размера qword, или можно и в более большие?

    Аргумент scanf и printf %lf тоже увещает в себе максимум qword???
     
  2. SII

    SII Воин против дзена

    Публикаций:
    0
    Представление (если угодно, кодирование) вещественных чисел прилично отличается от представления целых. Описано оно, например, в интеловских мануалах. Что же касается разрядности, то вещественные числа на ПК бывают 4-, 8- и 10-байтовыми.
     
  3. vasia

    vasia New Member

    Публикаций:
    0
    Форматы вещественных чисел можно посмотреть тут:
    http://en.wikipedia.org/wiki/IEEE_754-1985
    с одинарной точность, float, 32 бита, и с двойной точностью, double, 64 бит. Это хранение в памяти.

    Для арифметики на Интелах используюся 80-битные регистры сопроцессора.

    Использование спецификатора %lf для вещественных несколько неправильно. %f съедает как float, так и double. %Lf используется для long double (96 бит по умолчанию в gcc на Интеле, 128 бит например, на PowerPC).
     
  4. iZzz32

    iZzz32 Sergey Sfeli

    Публикаций:
    0
    vasia, нет. Во все функции на месте ... (ellipsis) в Си аргумент с плавающей точкой обязан передаваться как double. То есть, %f обозначает именно double и никаких float.
     
  5. vasia

    vasia New Member

    Публикаций:
    0
    iZzz32
    Век живи, век учись. Хотя, подозревал что-то подобное.
     
  6. Antolflash

    Antolflash New Member

    Публикаций:
    0
    Хм... как раз-таки для вывода float нужно printf("%f", floatvar), а для double катит только printf("%lf", doublevar)
    Любые другие комбинации были опробованы и выводили полный бред (длиннющее число в две строки консоли, никак не совпадающее, с тем, чем надо), причём, понятное дело, как в фасме, так и в студии.


    Вопрос об использовании tbyte или tword (кстати, в чём разница между ними в фасме, ведь оба 80 бит, и один из них, помоему, объявляется как dt qword, word ? ) остаётся открытом. Привидите пример кода, где можно scanf и printf tbyteовую ячейку памяти. Сейчас попробую пооперировать сопроцессором с такими ячейками.
     
  7. BlackParrot

    BlackParrot New Member

    Публикаций:
    0
    Код (Text):
    1. format  pe console
    2. entry   start
    3. include '../../include/win32a.inc'
    4.  
    5. start:
    6.  
    7. ;-----------------------------------------------------------------------
    8.       push dword [x+4]
    9.       push dword [x]
    10.       push fm
    11.       call [printf]
    12.       add esp,4*3
    13.  
    14.         retn
    15. ;-------------------------------------------------------------
    16. x dq 4f // 4.0
    17. fm db '%f',0;
    18.  
    19. ;--------------------------------------------------------------------
    20. data    import
    21.         library msvcrt,'msvcrt.dll'
    22.         import  msvcrt,\
    23.                 printf,'printf',\
    24.                 scanf,'scanf'
    25.         end     data
    Выводит double.

    Нет разницы. tbyte и tword модификаторы поинтера, не надо их путать с директивами резервирования помяти. И модификаторы эти эквивалетны. В более старом fasm было только tword. Вот пример
    Код (Text):
    1.       fld dword [eax]
    2.       fld tbyte [eax]
    3.       fld tword [eax]
    Последний два будут одинаковы.

    Код (Text):
    1. int main(int argc, char* argv[])
    2. {
    3.     double x = 1.1;
    4.     long double y = 0;
    5.  
    6.     x = y;
    7.     return 0;
    8. }
    Судя по дизасм-коду VC6 double и long double одинаковы.
     
  8. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Antolflash
    Посмотри здесь разжевано, как можно обойтись и без printf
     
  9. Antolflash

    Antolflash New Member

    Публикаций:
    0
    Код (Text):
    1. void main()
    2. {
    3.     double x;
    4.     scanf("%lf", &x);
    5.     printf("%f",x);
    6. }
    В printf %f корректно выводит double переменную. Но вот если в scanf передовать не "%lf", а "%f", то будет выведенна охинея.
     
  10. vasia

    vasia New Member

    Публикаций:
    0
    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)
     
  11. Antolflash

    Antolflash New Member

    Публикаций:
    0
    Я тут написал программу для возведения вещественного числа x в вещественную степень y.

    Код (Text):
    1.                                format  pe console
    2.         entry   start
    3.         include 'C:\FASM_WINDOWS\include\win32a.inc'
    4.  
    5. start:
    6.  
    7.  
    8.       push x
    9.       push fms
    10.       call [scanf]
    11.       add esp,4*2
    12.  
    13.       push y
    14.       push fms
    15.       call [scanf]
    16.       add esp,4*2
    17. ;-----------------------------------------------
    18.         finit
    19.         fld [one]
    20.         fld [x]
    21.         fld [zero]
    22.         xor ecx,ecx
    23.         fcomip st1
    24.         jb th
    25.         je zerocase
    26.         mov ecx,1
    27.         fabs
    28. th:     push ecx
    29.         fyl2x
    30.         fld [y]
    31.         fmul st0,st1
    32.         fst [ylog2x]
    33.         fld [one]
    34.         fxch
    35.         fprem
    36.         fst [osta]
    37.         fld [ylog2x]
    38.         fsub st0,st1
    39.         fistp [cel]
    40.         f2xm1
    41.         fld [one]
    42.         fadd st0,st1
    43.         fst [tpm]
    44.  
    45.         mov ebx,[cel]
    46.         cmp ebx,0
    47.         jg multiply
    48.         jl division
    49.         fstp [resultat]
    50.         jmp exi
    51.  
    52.  
    53. multiply: fld [two]
    54.           fld [one]
    55.  
    56. mult:        dec ebx
    57.              fmul st0,st1
    58.              test ebx,ebx
    59.              jnz mult
    60.              jmp semi
    61.  
    62.  
    63. division: fld [two]
    64.           fld [one]
    65.  
    66. divi:        inc ebx
    67.              fdiv st0,st1
    68.              test ebx,ebx
    69.              jnz divi
    70.  
    71.  
    72.  
    73.  
    74.  
    75.  
    76.   semi: fld [tpm]
    77.         fmul st0,st1
    78.         pop ecx
    79.         test ecx,ecx
    80.         jz skip
    81.         fld [minus]
    82.         fmul st0,st1
    83.  
    84.   skip: fstp [resultat]
    85.         jmp exi
    86.  
    87.  
    88.   zerocase:     fild [zero]
    89.                 fistp[resultat]
    90.  
    91.  exi:
    92. ;-----------------------------------------------------------------------
    93.       push dword[resultat+4]
    94.       push dword[resultat+0]
    95.       push fmp
    96.       call [printf]
    97.       add esp,4*3
    98.  
    99.  jmp start
    100.  
    101.         retn
    102. ;-------------------------------------------------------------
    103. x dq ?
    104. y dq  ?
    105. one dt 1.0
    106. two dq 2.0
    107. osta dq ?
    108. cel dd ?
    109. ylog2x dq ?
    110. tpm dq ?
    111. resultat dq ?
    112. fms db '%lf',0;
    113. fmp db '%lf',0x0d,0x0a,0;
    114. zero dq 0.0
    115. minus dq -1.0
    116.  
    117.  
    118.  
    119.  
    120. ;--------------------------------------------------------------------
    121. data    import
    122.         library msvcrt,'msvcrt.dll'
    123.         import  msvcrt,\
    124.                 printf,'printf',\
    125.                 scanf,'scanf'
    126.         end     data
    Есть два вопроса. Первый - мне захотелось, чтобы в случаи целого результата вывод был целым. И второй, как добиться того, чтобы в сопроцессор можно было загружать и оперировать числами tbyte, т.е. 80 бит, а не qword 60 бит.
    Данный ниже код отличается от верхнего проверкой на нулевой x (работает корректно), и проверкой на целость результата и вывод в целом формате. Вот тут то и косяк, не работающее место кода помеченно. Помогите пожалуйста разобраться, почему
    Код (Text):
    1.                                format  pe console
    2.         entry   start
    3.         include 'C:\FASM_WINDOWS\include\win32a.inc'
    4.  
    5. start:
    6.  
    7.  
    8.       push x
    9.       push fms
    10.       call [scanf]
    11.       add esp,4*2
    12.  
    13.       push y
    14.       push fms
    15.       call [scanf]
    16.       add esp,4*2
    17. ;-----------------------------------------------
    18.         finit
    19.         fld [one]
    20.         fld [x]
    21.         fld [zero]
    22.         xor ecx,ecx
    23.         fcomip st1
    24.         jb th
    25.         je zerocase
    26.         mov ecx,1
    27.         fabs
    28. th:     push ecx
    29.         fyl2x
    30.         fld [y]
    31.         fmul st0,st1
    32.         fst [ylog2x]
    33.         fld [one]
    34.         fxch
    35.         fprem
    36.         fst [osta]
    37.         fld [ylog2x]
    38.         fsub st0,st1
    39.         fistp [cel]
    40.         f2xm1
    41.         fld [one]
    42.         fadd st0,st1
    43.         fst [tpm]
    44.  
    45.         mov ebx,[cel]
    46.         cmp ebx,0
    47.         jg multiply
    48.         jl division
    49.         fst [resultat]
    50.         jmp exi
    51.  
    52.  
    53. multiply: fld [two]
    54.           fld [one]
    55.  
    56. mult:        dec ebx
    57.              fmul st0,st1
    58.              test ebx,ebx
    59.              jnz mult
    60.              jmp semi
    61.  
    62.  
    63. division: fld [two]
    64.           fld [one]
    65.  
    66. divi:        inc ebx
    67.              fdiv st0,st1
    68.              test ebx,ebx
    69.              jnz divi
    70.  
    71.  
    72.  
    73.  
    74.  
    75.  
    76.   semi: fld [tpm]
    77.         fmul st0,st1
    78.         pop ecx
    79.         test ecx,ecx
    80.         jz skip
    81.         fld [minus]
    82.         fmul st0,st1
    83.  
    84.   skip: fst [resultat]
    85.         jmp exi
    86.  
    87.  
    88.   zerocase:     fild [zero]
    89.                 fist[intres]
    90.                 jmp ze
    91.  
    92.  exi:
    93.   fld[one]
    94.   fxch
    95.   fprem
    96.   ftst
    97.   jnz exii
    98.   jmp pri
    99.  
    100. exii:
    101. ;-----------------------------------------------------------------------
    102.       push dword[resultat+4]
    103.       push dword[resultat+0]
    104.       push fmp
    105.       call [printf]
    106.       add esp,4*3
    107.  
    108.  jmp start
    109.  
    110.  
    111.  
    112. pri: fld [resultat]; КОСЯК!!!! НЕ ХОЧЕТ ЗАГРУЖАТЬ!!!!! ХОТЯ В [resultat] ЛЕЖИТ КОРРЕКТНОЕ ЧИСЛО
    113.      fistp [intres]
    114. ze:  push [intres]
    115.      push intfmp
    116.      call [printf]
    117.      add esp,4*2
    118.      jmp start
    119.  
    120.  
    121.         retn
    122. ;-------------------------------------------------------------
    123. x dq ?
    124. y dq  ?
    125. one dt 1.0
    126. two dq 2.0
    127. osta dq ?
    128. cel dd ?
    129. ylog2x dq ?
    130. tpm dq ?
    131. resultat dq ?
    132. fms db '%lf',0;
    133. fmp db '%lf',0x0d,0x0a,0;
    134. zero dq 0.0
    135. minus dq -1.0
    136. intres dd ?
    137. intfmp db '%i',0x0d,0x0a,0
    138.  
    139.  
    140.  
    141.  
    142. ;--------------------------------------------------------------------
    143. data    import
    144.         library msvcrt,'msvcrt.dll'
    145.         import  msvcrt,\
    146.                 printf,'printf',\
    147.                 scanf,'scanf'
    148.         end     data
     
  12. leo

    leo Active Member

    Публикаций:
    0
    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)
     
  13. Antolflash

    Antolflash New Member

    Публикаций:
    0
    Писал в учебных целях, лаба такая.
    Спасибо, учёл.
    Код (Text):
    1.                                format  pe console
    2.         entry   start
    3.         include 'C:\FASM_WINDOWS\include\win32a.inc'
    4.  
    5. start:
    6.  
    7.  
    8.       push x
    9.       push fms
    10.       call [scanf]
    11.       add esp,4*2
    12.  
    13.       push y
    14.       push fms
    15.       call [scanf]
    16.       add esp,4*2
    17. ;-----------------------------------------------
    18.         finit
    19.         fld1
    20.         fld [x]
    21.         fldz
    22.         xor ecx,ecx
    23.         fcomip st1
    24.         jb th
    25.         je zerocase
    26.         mov ecx,1
    27.         fabs
    28. th:     push ecx
    29.         fyl2x
    30.         fld [y]
    31.         fmul st0,st1
    32.         fst [ylog2x]
    33.         fld1
    34.         fxch
    35.         fprem
    36.         fst [osta]
    37.         fld [ylog2x]
    38.         fsub st0,st1
    39.         fistp [cel]
    40.         f2xm1
    41.         fld1
    42.         fadd st0,st1
    43.         fst [tpm]
    44.  
    45.         mov ebx,[cel]
    46.         cmp ebx,0
    47.         jg multiply
    48.         jl division
    49.         fst [resultat]
    50.         jmp exi
    51.  
    52.  
    53. multiply: fld [two]
    54.           fld1
    55.  
    56. mult:        dec ebx
    57.              fmul st0,st1
    58.              test ebx,ebx
    59.              jnz mult
    60.              jmp semi
    61.  
    62.  
    63. division:    fld [half]
    64.              fld1
    65.  
    66. divi:        inc ebx
    67.              fmul st0,st1
    68.              test ebx,ebx
    69.              jnz divi
    70.  
    71.  
    72.  
    73.  
    74.  
    75.  
    76.   semi: fld [tpm]
    77.         fmul st0,st1
    78.         pop ecx
    79.         test ecx,ecx
    80.         jz skip
    81.         fchs
    82.  
    83.   skip: fst [resultat]
    84.         jmp exi
    85.  
    86.  
    87.   zerocase:     fldz
    88.                 fist[intres]
    89.                 jmp ze
    90.  
    91.  exi:
    92.   fld1
    93.   fxch
    94.   fprem
    95.   fxch
    96.   fstp [musor]
    97.   fldz
    98.   fcomip st1
    99.   jnz exii
    100.   jmp pri
    101.  
    102. exii:
    103. ;-----------------------------------------------------------------------
    104.       push dword[resultat+4]
    105.       push dword[resultat+0]
    106.       push fmp
    107.       call [printf]
    108.       add esp,4*3
    109.  
    110.  jmp start
    111.  
    112.  
    113.  
    114. pri: fistp[musor]
    115. fld [resultat]
    116.      fistp [intres]
    117. ze:  push [intres]
    118.      push intfmp
    119.      call [printf]
    120.      add esp,4*2
    121.      jmp start
    122.  
    123.  
    124.         retn
    125. ;-------------------------------------------------------------
    126. x dq ?
    127. y dq  ?
    128. half dd 0.5
    129. two dd 2.0
    130. osta dq ?
    131. cel dd ?
    132. ylog2x dq ?
    133. tpm dq ?
    134. resultat dq ?
    135. fms db '%lf',0;
    136. fmp db '%f',0x0d,0x0a,0;
    137. intres dd ?
    138. intfmp db '%i',0x0d,0x0a,0
    139. musor dq ?
    140.  
    141.  
    142.  
    143.  
    144. ;--------------------------------------------------------------------
    145. data    import
    146.         library msvcrt,'msvcrt.dll'
    147.         import  msvcrt,\
    148.                 printf,'printf',\
    149.                 scanf,'scanf'
    150.         end     data
    Только вот вопрос с использование TBYTE открыт. Команда fst [tbyte_var] не работает
     
  14. Antolflash

    Antolflash New Member

    Публикаций:
    0
    Вопрос решён
    Команда fst копирует значение из вершины стека сопроцессора в операнд-адресат, которым может быть 32-битное или 64-битное расположение в памяти или другой регистр FPU. fstp совершает ту же операцию, но далее выталкивает значение из стека, освобождая ST(0). fstp может сохранять ещё и 80-битное значение в память.
     
  15. Antolflash

    Antolflash New Member

    Публикаций:
    0
    Однако передача Lf в scanf и дальнейший fld[tbyte_var] посылает на вершину стека bad значение(
     
  16. leo

    leo Active Member

    Публикаций:
    0
    На вскидку видно, что ни фига ты не учел - добавил одну\другую очистку, ошибка пропала и все. По правилам программирования fpu после выполнения вычислений стек fpu должен быть полностью очищен, иначе это может аукнуться в дальнейшем. А ты вместо нормальной работы со стеком полагаешься на грубую силу - тормозную операцию finit, которую обычно в программе вызывают всего один раз, а не в начале каждого цикла. В общем, "садись, два !" :)
     
  17. leo

    leo Active Member

    Публикаций:
    0
    Тебе уже объяснили, что scanf и printf работают только с double и соотв-но Lf это тоже самое, что просто f
     
  18. Antolflash

    Antolflash New Member

    Публикаций:
    0
    А кто работает с long double??? Самому писать ввод-вывод? Наверно придётся разбираться с портами ввода-вывода.
     
  19. l_inc

    l_inc New Member

    Публикаций:
    0
    Antolflash
    Шутка удалась.
    ReadConsole/WriteConsole + функции перевода числа в строку и строки в число.
     
  20. Antolflash

    Antolflash New Member

    Публикаций:
    0
    Хм... Я было начал этим заниматься, но бросил изучение WIN API.
    Так что получается, в обычном C есть тип long double, но нет стандартных библиотечных средств его ввода-вывода?