Как устроены StrToFloat и StrToInt?

Тема в разделе "WASM.BEGINNERS", создана пользователем Adrax, 20 янв 2007.

  1. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    Уважаемые программисты! Нуждаюсь в собственной ассемблерной реализации функций StrToInt и StrToFloat для перевода введенной строки символов в числовую форму
    К сожалению, не обладаю достаточной квалификацией для самостоятельного дизассемблирования и реверсинга этих функций, поэтому прошу вашего совета
    Буду благодарен любой подсказке
     
  2. IceFire

    IceFire New Member

    Публикаций:
    0
    Регистрация:
    30 окт 2006
    Сообщения:
    244
    1. Определяем, что является разделителем целой и дробной части числа (точка или зпт)
    2. Находим номер ее позиции.
    3. Все, что слева, запишем в отдельную строку, справа - в отдельную
    4. Посчитаем кол-во символов там и там.
    5. Ну и поехали: берем первый байт левой строки, вычитам из него ASCII-код символа "0" (или "1" (?) - не помню, посмотри в таблице, что раньше идет), полученное значение умножаем на 10 в степени кол-ва знаков в левой подстроке минус текущая позиция, кидаем результат в накопитель (прибавляем к нему), затем - знак во второй позиции левой строки и т.д.
    6. Для правой строки то же самое, ток умножаем на 10 в степени (-(кол-во знаков в правой подстроке минус текущая позиция)).
    7. Фсе.
     
  3. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    Посмотри исходники KOL, там есть вариант и на асме, и на паскале.
     
  4. W4FhLF

    W4FhLF New Member

    Публикаций:
    0
    Регистрация:
    3 дек 2006
    Сообщения:
    1.050
    \masm32\m32lib\ATOFP.ASM
    \masm32\m32lib\ATODW.ASM

    За основу возьми.
     
  5. TourerV

    TourerV New Member

    Публикаций:
    0
    Регистрация:
    18 дек 2006
    Сообщения:
    1
    где это посмотреть?
     
  6. n0name

    n0name New Member

    Публикаций:
    0
    Регистрация:
    5 июн 2004
    Сообщения:
    4.336
    Адрес:
    Russia
    http://www.kolnmck.ru/start.shtml
     
  7. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    Уважаемые программисты! Огромное спасибо за советы!
    Правда в исходниках KOL я не нашёл ничего подобного, но, ковыряя ATOFP и исходники одного калькулятора, дошёл до истины. Перевожу сейчас с масма на родной фасм...
     
  8. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    Прошу прощения, возник ещё вопрос.
    Во-первых, правильно ли я делаю:
    Код (Text):
    1. finit
    2. fldz
    3. cycle:
    4. xor eax,eax
    5. mov al,byte [esi]
    6. inc esi
    7. sub al,30h
    8. jb error
    9. cmp al,9
    10. ja error
    11. mov [chislo],eax
    12. fld [ten]
    13. fmulp st1,st
    14. fld [chislo]
    15. faddp st1,st
    16. loop cycle
    17. fstp [num10]
    Во-вторых, почему, если я ввожу, например, число 1, в st0 грузится 1.4 с бешеным хвостом и порядком -45? Это нормально?
    И ещё: а как вывести число? Т.е. как реализовать FloatToString?
     
  9. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Adrax
    Во-первых = во-вторых = неправильно ;) Для работы с целыми числами нужно использовать инструкции fiXXX, тогда и 1 будет 1.0, а не "1.4 с бешеным хвостом"
    Дробление операций на fld + fXXX st1,st лишь увеличивает размер кода и нагрузку на декодер, лучше сразу использовать инструкции с операндами памяти
    Код (Text):
    1.   xor eax,eax  ;один раз перед циклом
    2. cycle:
    3.   mov al,[esi]
    4.   inc esi
    5.   sub al,"0"            ;jb тут не нужен - лишняя операция
    6.   cmp al,9
    7.   ja error
    8.   mov [chislo],eax
    9.   fmul [ten]           ;ten dq 10.0
    10.   fiadd [chislo]       ;chislo dd 0
    11.   jmp cycle           ;loop cycle тут ни к селу, ни к городу ;)
    Да все там же - \masm32\m32lib\Fptoa.asm
     
  10. crypto

    crypto Active Member

    Публикаций:
    0
    Регистрация:
    13 дек 2005
    Сообщения:
    2.533
    Adrax
    Посмотри, как это сделано у Борланда в Дельфи7 (модуль SysUtils.pas)
    Код (Text):
    1. function TextToFloat(Buffer: PChar; var Value;
    2.   ValueType: TFloatValue): Boolean;
    3.  
    4. const
    5. // 8087 control word
    6. // Infinity control  = 1 Affine
    7. // Rounding Control  = 0 Round to nearest or even
    8. // Precision Control = 3 64 bits
    9. // All interrupts masked
    10.   CWNear: Word = $133F;
    11.  
    12. var
    13.   Temp: Integer;
    14.   CtrlWord: Word;
    15.   DecimalSep: Char;
    16.   SaveGOT: Integer;
    17. asm
    18.         PUSH    EDI
    19.         PUSH    ESI
    20.         PUSH    EBX
    21.         MOV     ESI,EAX
    22.         MOV     EDI,EDX
    23. {$IFDEF PIC}
    24.         PUSH    ECX
    25.         CALL    GetGOT
    26.         POP     EBX
    27.         MOV     SaveGOT,EAX
    28.         MOV     ECX,[EAX].OFFSET DecimalSeparator
    29.         MOV     CL,[ECX].Byte
    30.         MOV     DecimalSep,CL
    31. {$ELSE}
    32.         MOV     SaveGOT,0
    33.         MOV     AL,DecimalSeparator
    34.         MOV     DecimalSep,AL
    35.         MOV     EBX,ECX
    36. {$ENDIF}
    37.         FSTCW   CtrlWord
    38.         FCLEX
    39. {$IFDEF PIC}
    40.         FLDCW   [EAX].CWNear
    41. {$ELSE}
    42.         FLDCW   CWNear
    43. {$ENDIF}
    44.         FLDZ
    45.         CALL    @@SkipBlanks
    46.         MOV     BH, byte ptr [ESI]
    47.         CMP     BH,'+'
    48.         JE      @@1
    49.         CMP     BH,'-'
    50.         JNE     @@2
    51. @@1:    INC     ESI
    52. @@2:    MOV     ECX,ESI
    53.         CALL    @@GetDigitStr
    54.         XOR     EDX,EDX
    55.         MOV     AL,[ESI]
    56.         CMP     AL,DecimalSep
    57.         JNE     @@3
    58.         INC     ESI
    59.         CALL    @@GetDigitStr
    60.         NEG     EDX
    61. @@3:    CMP     ECX,ESI
    62.         JE      @@9
    63.         MOV     AL, byte ptr [ESI]
    64.         AND     AL,0DFH
    65.         CMP     AL,'E'
    66.         JNE     @@4
    67.         INC     ESI
    68.         PUSH    EDX
    69.         CALL    @@GetExponent
    70.         POP     EAX
    71.         ADD     EDX,EAX
    72. @@4:    CALL    @@SkipBlanks
    73.         CMP     BYTE PTR [ESI],0
    74.         JNE     @@9
    75.         MOV     EAX,EDX
    76.         CMP     BL,fvCurrency
    77.         JNE     @@5
    78.         ADD     EAX,4
    79. @@5:    PUSH    EBX
    80.         MOV     EBX,SaveGOT
    81.         CALL    FPower10
    82.         POP     EBX
    83.         CMP     BH,'-'
    84.         JNE     @@6
    85.         FCHS
    86. @@6:    CMP     BL,fvExtended
    87.         JE      @@7
    88.         FISTP   QWORD PTR [EDI]
    89.         JMP     @@8
    90. @@7:    FSTP    TBYTE PTR [EDI]
    91. @@8:    FSTSW   AX
    92.         TEST    AX,mIE+mOE
    93.         JNE     @@10
    94.         MOV     AL,1
    95.         JMP     @@11
    96. @@9:    FSTP    ST(0)
    97. @@10:   XOR     EAX,EAX
    98. @@11:   FCLEX
    99.         FLDCW   CtrlWord
    100.         FWAIT
    101.         JMP     @@Exit
    102.  
    103. @@SkipBlanks:
    104.  
    105. @@21:   LODSB
    106.         OR      AL,AL
    107.         JE      @@22
    108.         CMP     AL,' '
    109.         JE      @@21
    110. @@22:   DEC     ESI
    111.         RET
    112.  
    113. // Process string of digits
    114. // Out EDX = Digit count
    115.  
    116. @@GetDigitStr:
    117.  
    118.         XOR     EAX,EAX
    119.         XOR     EDX,EDX
    120. @@31:   LODSB
    121.         SUB     AL,'0'+10
    122.         ADD     AL,10
    123.         JNC     @@32
    124. {$IFDEF PIC}
    125.         XCHG    SaveGOT,EBX
    126.         FIMUL   [EBX].DCon10
    127.         XCHG    SaveGOT,EBX
    128. {$ELSE}
    129.         FIMUL   DCon10
    130. {$ENDIF}
    131.         MOV     Temp,EAX
    132.         FIADD   Temp
    133.         INC     EDX
    134.         JMP     @@31
    135. @@32:   DEC     ESI
    136.         RET
    137.  
    138. // Get exponent
    139. // Out EDX = Exponent (-4999..4999)
    140.  
    141. @@GetExponent:
    142.  
    143.         XOR     EAX,EAX
    144.         XOR     EDX,EDX
    145.         MOV     CL, byte ptr [ESI]
    146.         CMP     CL,'+'
    147.         JE      @@41
    148.         CMP     CL,'-'
    149.         JNE     @@42
    150. @@41:   INC     ESI
    151. @@42:   MOV     AL, byte ptr [ESI]
    152.         SUB     AL,'0'+10
    153.         ADD     AL,10
    154.         JNC     @@43
    155.         INC     ESI
    156.         IMUL    EDX,10
    157.         ADD     EDX,EAX
    158.         CMP     EDX,500
    159.         JB      @@42
    160. @@43:   CMP     CL,'-'
    161.         JNE     @@44
    162.         NEG     EDX
    163. @@44:   RET
    164.  
    165. @@Exit:
    166.         POP     EBX
    167.         POP     ESI
    168.         POP     EDI
    169. end;
     
  11. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    Огромное спасибо! Только начал постигать десятичную арифметику в ассемблере - поначалу очень сложно...
     
  12. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    Уважаемые программисты! Понимаю, что всех уже достал, но - если ввод и преобразование числа я ещё понял, то с выводом возникли проблемы. Вот как я принимаю число:
    Код (Text):
    1. format PE console
    2. include 'win32axp.inc'
    3.  
    4. section '.data' data readable writeable
    5. ns dd ?
    6. hout dd ?
    7. buffer db 51 dup (?)
    8. help db 'Usage: con_4islo.exe NUMBER',0
    9. Retry db 'Program fails. Please, retry',0
    10. num10 dt ?
    11. chislo dd ?
    12. ten dd 10
    13. minus db ?
    14. status dw ?
    15. to4ka dd ?
    16.  
    17. .code
    18. fuck:
    19. invoke GetStdHandle,STD_OUTPUT_HANDLE
    20. mov [hout],eax
    21. invoke GetCommandLine
    22. mov esi,eax
    23. cycle1:
    24.  cmp byte [esi],20h
    25.  je parameter
    26.  cmp byte [esi],0Dh
    27.  je najobka
    28.  inc esi
    29.  jmp cycle1
    30. parameter:
    31.  mov edi,buffer
    32.  mov ecx,50
    33.    cycle2:
    34.      inc esi
    35.      mov al,byte [esi]
    36.      cmp al,0Dh
    37.      je konets
    38.      mov byte [edi],al
    39.      inc edi
    40.      loop cycle2
    41.  
    42. konets:
    43. invoke lstrlen,buffer
    44. mov ecx,eax
    45. mov esi,buffer
    46. mov edi,esi
    47. add edi,ecx
    48. dec edi
    49. xor eax,eax
    50.  
    51.  
    52. finit
    53. fldz
    54. mov al,byte [esi]
    55. cmp al,2Dh
    56. je otric
    57. cycle3:
    58.   mov al,byte [esi]
    59.   inc esi
    60.   cmp al,2Eh
    61.   je drobnoe
    62.   cmp al,2Ch
    63.   je drobnoe
    64.   sub al,30h
    65.   jb retry
    66.   cmp al,9
    67.   ja retry
    68.   mov [chislo],eax
    69.   fild [ten]
    70.   fmulp st1,st
    71.   fild [chislo]
    72.   faddp st1,st
    73.   loop cycle3
    74.  fldz
    75.  jmp NoMore
    76.  
    77. drobnoe:
    78.   dec ecx
    79.   xor eax,eax
    80.   fldz
    81.  cycle4:
    82.     mov al,byte [edi]
    83.     dec edi
    84.     sub al,30h
    85.     jb retry
    86.     cmp al,9
    87.     ja retry
    88.     mov [chislo],eax
    89.     fild [chislo]
    90.     faddp
    91.     fild [ten]
    92.     fdivp st1,st
    93.     loop cycle4
    94.  
    95. NoMore:
    96.   faddp st1,st
    97.   xor eax,eax
    98.   mov al,[minus]
    99.   test al,al
    100.   jz zagruz
    101.   fchs
    102. zagruz:
    103.   fstp tbyte [num10]
    104. exit:
    105. invoke ExitProcess,0
    106.  
    107. najobka:
    108.  invoke WriteConsole,[hout],help,27,ns,NULL
    109.  
    110.  
    111.  
    112. retry:
    113.  invoke WriteConsole,[hout],Retry,28,ns,NULL
    114.  jmp exit
    115.  
    116. otric:
    117. mov [minus],1
    118. dec ecx
    119. inc esi
    120. jmp cycle3
    121.  
    122. .end fuck
    И вот, я положил число в переменную num10 типа tbyte (это BCD формат, я так понял?) - я в принципе могу грузануть её обратно в стек FPU, провести необходимые арифметические операции, но как мне преобразовать это число в строку?
    Пожалуйста, не отсылайте меня к масмовским либам, а поясните хотя бы в общих словах алгоритм разбора BCD. Я, наверное, сам смогу вывести цифры в строку, но как определить десятичный порядок и правильно поставить разделитель-точку?
    Прошу вашей помощи
     
  13. Zhelezovsky

    Zhelezovsky Member

    Публикаций:
    0
    Регистрация:
    24 окт 2006
    Сообщения:
    39
    А FPU разве работает с BCD-форматом? С ним действий-то: +, -, *, / и коррекция результата (daa). А всё остальное, включая и учёт переноса-заёма - дело программиста. Почитай про BCD-числа у Юрова.
     
  14. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    Гм... Повтыкал в мануалы - у меня ж не BCD получается, а число в формате extended, если я правильно понял... Как преобразовать его в строку?
    Видел несколько реализаций, но там число выводится в экспоненциальной форме. А как его вывести просто, как дробное, с запятой? Как определить позицию запятой (т.е. определить десятичный порядок?)? Помогите хотя бы алгоритмом, а то никак не пойму...
     
  15. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Adrax
    Уж и не знаю, где тебе отвечать - здесь или тут ;)
    Короче так, если ты хочешь замутить универсальную функцию с анализом порядка числа, то разбирайся в готовых реализациях (masm, delphi и т.п.) или бери их один к одному - лучше\проще все равно не сделаешь ;) А вот сделать (достаточно) простой вариант вывода числа в децимальном виде с заданным числом знаков после запятой можно. В этом случае "определять десятичный порядок" не нужно, т.к. предполагается что пользователь сам задает "разумное" число знаков после запятой исходя из ограничения, что суммарное число десятичных цифр целой и дробной части не должно превышать 18-ти.
    В этом случае алгоритм будет примерно таким:
    1) создаем таблицу степеней 1.0E1, 1.0E2, ..., 1.E17
    2) умножаем число на степень в соответствии с заданным числом n знаков после запятой (считаем, что fpu-исключения замаскированы finit)
    3) сохраняем число в BCD-формате (fbstp)
    4) проверяем старший (10-й) байт BCD на 0FFh - если равно, значит переполнение (взято слишком большое n) - грязно ругаемся и выходим :lol: (ну или уменьшаем n и повторяем попытку)
    5) проверяем знак старшего байта - если установлен, то пишем '-' в строку
    Дальше есть два варианта - либо сразу анализировать BCD и сохранять готовые цифры в строку (получается более навороченно), либо по простому сначала преобразовать весь BCD в промежуточную строку, а затем переписать ее с пропуском лидирующих нулей целой части и вставкой десятичной точки перед n-ным символом от конца строки. Поскольку скорость тут особо не нужна, то как правило используется именно второй вариант. <..deleted..> Попробуй такой вариант - там посмотрим ;)
     
  16. Zhelezovsky

    Zhelezovsky Member

    Публикаций:
    0
    Регистрация:
    24 окт 2006
    Сообщения:
    39
    Ех, работает FPU... С упакованными BCD-числами, сорри. :dntknw:
     
  17. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    2 leo
    Огромное спасибо!! Просто, когда мне на словах распишут, как и что - на код переложить легче
    Я понял твои слова, но решил не делать таблицу, а возводить десять в нужную степень в отдельной процедуре... Опять столкнулся с траблами (не одно, так другое) - запутался в округлении до целого
    Вынес этот вопрос в отдельную тему "Округление в FPU" (здесь же, в BEGINNERS) - посмотри, там код этой процедуры - возможно, что-то не так?
     
  18. Adrax

    Adrax Алексей

    Публикаций:
    0
    Регистрация:
    14 окт 2006
    Сообщения:
    135
    Адрес:
    г. Курск
    leo
    Гм... Понимаю, что достал... Спасибо за твои советы, мучаюсь потихоньку и снова задаю вопросы
    Решил попытать табличный метод: заделал 18 qword'ов с разными степенями десяти и задумался: как же быстро вытащить из массива требуемое значение? Ну и решил забить их в стек по порядку, потом вычисляю, сколько цифр после запятой (сохраняю в esi), затем обрезаю всё ненужное (esp+=esi), ну и гружу qword [esp] в сопр, где и множу...
    Трабла: хоть и юзаю fild, всё равно грузит какую-то белиберду типа 1,3657489... с экспонентой. Это из-за явного указания размера? А без него фасм не компилит!!
     
  19. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Потому и белиберду :) для 1.0E1, 1.0E2, ..., 1.E17 FLD предназначена ;)
    И зачем копировать всю таблицу в стек когда всё что нужно - это взять из неё одно число?
    Мне в плане оптимизации до leo конечно далеко, но имхо тут нужно что-то типа
    FMUL [TableName + eax*8], где eax = количество знаков после запятой
     
  20. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Adrax
    FPU числа бывают:
    обычной точности DWORD - десятичный порядок +-38, двоичная мантиса = 24 бита
    двойной точности QWORD - десятичный порядок +-308, двоичная мантиса = 53 бита
    расширенной точности TBYTE - десятичный порядок +-4932, двоичная мантиса = 64 бита
    В данном случае мах = 1.0E17 и DWORD сойдёт, QWORD всё равно недоиспольуется.
    Только не путай FPU DWORD c обычным целым DWORD ;)

    Кстати leo давно хотел спросить есть ли разница в скорости загрузки\сохранения при работе с числами разной точности?