Код магнитолы

JS - телефон

Оппозиция в России ?

Россия и Украина

Мысли в слух...

JS - часы

Российская...власть ?

Skype

Создаём сайт

Любимые сериалы

Гостевая


На главную

Очень вещественная арифметика

Попробуем-таки добавить на наш арифмометр кнопку с символом "точка". Графику трогать не будем - остановимся только на арифметике, например - сложение:
           ...
           add_32  proc                      
           finit              
           fld  float1     ;float1  dd  ?        
           fld  float      ;float   dd  ?         
           fadd             
           fstp res        ;res  dq  ?
           ret
           add_32  endp
           ...                       

Преобразование строка - число

Сопроцессор работает и с целыми, и с дробными (вещественными) числами. Преобразование ASCII-> двоичный код, для целых чисел, мы уже рассматривали. Для дробных чисел - оно несколько сложнее. Но - не на много.
Когда числа задаются в .data, то процессор сам преобразует их в "удобоваримый" вид. В нашем случае - мы не знаем, какими будут эти числа? Поэтому, нам придётся учитывать возможность наличия в числе дробной части.
Вспомним принцип преобразования целого числа:
                (((0*10)+1)*10+2)*10+3=123 
Если идти по тропинке Горнера, то дробную часть можно преобразовать так:
                (((3*0.1+2)*0.1+1)*0.1+0=0.123 
После чего - сложить оба результата. Выглядит заманчиво, но...сопроцессор оперирует своим форматом чисел. И получается этот формат при использовании команд сопроцессора. Поэтому процедуру преобразования нам придётся несколько видоизменить.
Поскольку с преобразованием целого (целой части) числа мы уже знакомы, то остановимся поподробнее на преобразовании дробной части:
         принцип формирования дробной части float:
         число 0.123(.123)

 цикл:           1-й        2-й           3-й
 что делаем:   1*0.1+0   2*0.01+0.1   3*0.001+0.12
 итог:           0.1        0.12         0.123
 
                     рис.9.1
Если число меньше единицы (рис.9.1), то в вершине стека у нас будет ноль (st=0), котрый мы прибавляем в первом цикле. Если-же число больше единицы - в вершине стека будет целая часть числа.
А выглядеть это будет примерно так:
      pushad                ;сохраняем значения регистров
      mov  dot,0            ;инициируем dot
      lea  esi,stroka       ;строка символов
      lea  edi,float        ;вещественный результат
      finit                 ;обрабатываем необработанные исключения  
      fldz                  ;st=0
      cld                   ;устанавливаем направление переноса - от начала к концу цепочки	  
    cycl:
      xor  eax,eax          ;чистим регистр
      lodsb                 ;копируем байт/символ из DS:(E)SI в AL
      cmp  al,0             ;если нет символа
      je   fini             ;на выход 
      mov  dl,al            ;только для '.'
      and  al,0Fh           ;преобразуем  
      movzx eax,al          ;расширяем  
      mov  [edi],eax        ;и сохраняем 	  
      cmp  dot,0            ;была точка?
      jg   c_dec            ;если да (dot > 0)
      cmp  dl,'.'           ;это точка?
      je   c_dot            ;если да
 
    c_int:                  ;работаем с целой частью числа
      fimul ten             ;st=0*10...
      fiadd dword ptr [edi] ;st=0*10+float, накапливаем дв.код целой части 
      jmp  cycl             ;повторяем с целой частью
	  
    c_dot:                  ;встретили точку
      mov  dot,1h           ;фиксируем наличие точки и готовим множитель для дробной части
      fld1                  ;st=1                st(1)=float<-в начале=0
      fidiv ten             ;st=1:10=0.1         st(1)=float
      fstp aten             ;st=float            aten=0.1                    
      jmp  cycl             ;к след.символу 
	  
    c_dec:  
      fld  aten             ;st=0.1              st(1)<-в начале float=0
      fimul dword ptr [edi] ;st=0.1*1            st(1)=float
      faddp st(1),st        ;st=0.1*1+0...накапливаем дв.код дробной части        
      fld   aten            ;st=0.1              st(1)=float
      fidiv ten             ;st=0.1/10           st(1)=float
      fstp  aten            ;st=float            aten=0.01...
      jmp  cycl             ;повторяем с дробной частью   
	  
    fini:
      fstp dword ptr [edi]  ;сохраняем двоичный код во float
      popad
  
                                             рис.9.2  
Как видно из кода (рис.9.2) - ничего нового мы не придумали, просто подключили сопроцессор, а точнее, его команды.

Преобразование число - строка

Данное преобразование - вещь довольно мозголомная. После долгих поисков в сети я остановился на наиболее, на мой взгляд, доходчивом варианте. Его автор И.В.Лысков. Мы переделаем этот вариант под win32:
      
      pushad
      lea  edi,stroka
      finit                 ;инициируем FPU
      fld  res              ;st=res (в примере res=73.25)
      ftst                  ;st=0.0? 
      fstsw ax              ;копируем слово состояния, для основного процессора
      sahf                  ;запись АН в EFLAGS,копирует биты: С3->ZF, С2->PF, а С0->СF
      jnz  m1               ;если  res !=0 -> m1  
      mov  ax,'0'           ;добавляем на дисплей 0 -> результат арифметической операции = 0
      stosw                 ;перенос содержимого AL(AX,EAX)в адрес памяти ES:(E)DI
      jmp  m6               ;и - выходим
    m1:
      jnc  m2               ;если число '+', нет переноса
      mov  al,'-'           ;если есть перенос - число '-'
      stosb
      fchs                  ;изменяем знак числа
    ;                       st       st(1)     st(2)     st(3)   ...
    m2:                     ;73,25 - условное число, для наглядности
      fld1                  ;1       73,25                
      fld  st(1)            ;73,25     1       73,25      
      fprem                 ; 0,25     1       73,25      
      fsub st(2),st         ; 0,25     1       73         
      fxch st(2)            ;73        1        0,25      
      xor  ecx,ecx          ;ECX=0, готовим счётчик
    m3:
      fidiv ten             ; 7.3      1        0.25      
      fxch st(1)            ; 1        7.3      0.25      
      fld  st(1)            ; 7.3      1        7.3       0.25
      fprem                 ; 0.3      1        7.3       0.25
      fsub st(2),st         ; 0.3      1        7         0.25
      fimul ten             ; 3        1        7         0.25
      fistp dig             ; 1        7        0.25      
      inc  ecx              ;уменьшаем счётчик
      push dig              ;сохраняем цифру для преобразования (m4)
      fxch st(1)            ; 7        1        0.25      
      ftst                  ;st=0.0?
      fstsw ax
      sahf 
      jnz  m3               ;если цифра не 0 - повторяем
    m4:
      pop  eax              ;выталкиваем/копируем цифру в ЕАХ->AL
      add  al,30h           ;и преобразуем в символ
      stosb                 ;добавляем в stroka
      loop m4               ; 0        1        0.25      
    ;дробная часть:  если есть
      fstp st               ; 1        0.25            
      fxch st(1)            ; 0.25     1
      ftst
      fstsw ax
      sahf
      jz   m6               ;если дробная часть = 0
      mov  al,"."           ;добавляем точку в stroka
      stosb
      mov  ecx,six          ;заряжаем счётчик - 6 знаков после "."
    m5:
      fimul ten             ; 2.5       1
      fxch st(1)            ; 1         2.5
      fld  st(1)            ; 2.5       1       2.5
      fprem                 ; 0.5       1       2.5
      fsub st(2),st         ; 0.5       1       2
      fxch st(2)            ; 2         1       0.5
      fistp dig             ; 1         0.5
      mov  eax,dig          ;EAX = dig
      add  al,30h           ;преобразуем цифру 
      stosb                 ;и добавляем в stroka
      fxch st(1)            ; 0.5       1
      ftst                  ;если остаток не 0
      fstsw ax
      sahf                  ;повторяем по счётчику
      loopnz m5             ; 0         1 
    m6:
      fstp st               ; 1
      fstp st               ; ... почистились
      mov  byte ptr [edi],0 ;закрываем stroka
    fin:
      popad
	  
                                     рис.10
									 
Если внимательно присмотрется к коду (рис.10), то можно заметить, что арифмометр работает только с положительными числами. Что мы делаем:
- берётся итог арифметической операции
- из него выделяется целая часть m2
- эта целая часть в цикле m3 разбивается на отдельные цифры, которые сохраняются в памяти
- затем, в цикле m4, эти цифры преобразуются в ASCII-символы и добавляются в буфер stroka
- после этого проверяется наличие дробной части m4 и добавляется/не добавляется точка
- если дробная часть существует, то в цикле m5, она разбивается на отдельные цифры, максимальное количество (точность вычисления) которых мы задаём
- в этом-же цикле m5 цифры дробной части преобразуются и добавляются в буфер stroka
Заканчивая преобразование мы добавляем в буфер 0.

P.S.

Для желающих разобратся в представленном коде не спеша и без сети, можно скачать архив с файлами. Компоновка архивной программы несколько отличается от стандартного вида, но никто не запрещает переделать её по своему.

вперёд...
...назад