;
; функции для работы с числами float
;
; Количество знаков числа после запятой (1-17)
NumberSymbolsAD DW 5
; Константы (10 в степени N)
MConst: DQ 1.0E1,1.0E2,1.0E3,1.0E4,1.0E5
DQ 1.0E6,1.0E7,1.0E8,1.0E9,1.0E10
DQ 1.0E11,1.0E12,1.0E13,1.0E14,1.0E15
DQ 1.0E16,1.0E17,1.0E18,1.0E19,1.0E20
DQ 1.0E21,1.0E22,1.0E23,1.0E24,1.0E25
DQ 1.0E26,1.0E27,1.0E28,1.0E29,1.0E30
DQ 1.0E31,1.0E32,1.0E33,1.0E34,1.0E35
DQ 1.0E36,1.0E37,1.0E38,1.0E39,1.0E40
DQ 1.0E41,1.0E42,1.0E43,1.0E44,1.0E45
DQ 1.0E46,1.0E47,1.0E48,1.0E49,1.0E50
DQ 1.0E51,1.0E52,1.0E53,1.0E54,1.0E55
DQ 1.0E56,1.0E57,1.0E58,1.0E59,1.0E60
DQ 1.0E61,1.0E62,1.0E63,1.0E64,1.0E65
DQ 1.0E66,1.0E67,1.0E68,1.0E69,1.0E70
DQ 1.0E71,1.0E72,1.0E73,1.0E74,1.0E75
DQ 1.0E76,1.0E77,1.0E78,1.0E79,1.0E80
DQ 1.0E81,1.0E82,1.0E83,1.0E84,1.0E85
DQ 1.0E86,1.0E87,1.0E88,1.0E89,1.0E90
DQ 1.0E91,1.0E92,1.0E93,1.0E94,1.0E95
DQ 1.0E96,1.0E97,1.0E98,1.0E99,1.0E100
DQ 1.0E101,1.0E102,1.0E103,1.0E104,1.0E105
DQ 1.0E106,1.0E107,1.0E108,1.0E109,1.0E110
DQ 1.0E111,1.0E112,1.0E113,1.0E114,1.0E115
DQ 1.0E116,1.0E117,1.0E118,1.0E119,1.0E120
DQ 1.0E121,1.0E122,1.0E123,1.0E124,1.0E125
DQ 1.0E126,1.0E127,1.0E128
.end:
; Число с плавающей запятой двойной точности
Data_Double DQ ?
; Число в BCD-формате
Data_BCD DT ?
; Вспомогательный флаг
Data_Flag DB ?
; Знак результата (если не 0 - отрицательное число)
Data_Sign DB ?
; Знак результата - 0 для ..e+.. и 1 для ..e-..
Data_Sign_Exp DB ?
align 4
; Строка для хранения числа в коде ASCII
Data_String DB 32 DUP (?)
;*******************************************************
;* ПРЕОБРАЗОВАНИЕ ЧИСЛА С ПЛАВАЮЩЕЙ ЗАПЯТОЙ В СТРОКУ *
;* Число имеет формат с удвоенной точностью, результат *
;* выдается в десятичном коде, в "бытовом" формате с *
;* фиксированным количеством знаков после запятой. *
;* Входные параметры: *
;* Data_Double - преобразуемое число; *
;* NumberSymbolsAD - количество знаков после *
;* запятой (0-17). *
;* Выходные параметры: *
;* Data_String - строка-результат. *
;*******************************************************
align 4
DoubleFloat_to_String:
pushad
; Результат записывать в строку Data_String
mov EDI, Data_String
; Сдвигаем число влево на NumberSymbolsAD
; десятичных разрядов
fninit ;сброс сопроцессора
fld [Data_Double] ;загрузить число
xor ebx,ebx
mov BX,[NumberSymbolsAD]
cmp BX, 0
je .NoShifts ;нет цифр после запятой
jl .Error ;ошибка
dec BX
lea ebx,[MConst+8*ebx]
fmul qword [EBX] ;умножить на константу
.NoShifts:
; Извлечь число в коде BCD
fbstp [Data_BCD]
; Проверить результат на переполнение
mov AX,word [Data_BCD + 8]
cmp AX,0FFFFh ;"десятичное" переполнение?
je .Overflow
; Выделить знак числа и записать его в ASCII-коде
mov AL, byte [Data_BCD + 9]
and AL,AL
jz .NoSign
mov AL,'-'
stosb
.NoSign:
; Распаковать число в код ASCII
mov ebx,8 ;смещение последней пары цифр
mov ecx,9 ;счетчик пар цифр
; Определить позицию десятичной точки в числе
mov DX,18
sub DX,[NumberSymbolsAD]
js .Error ;ошибка, если отрицательная
jz .Error ;или нулевая позиция
.NextPair:
; Загрузить очередную пару разрядов
mov AL, byte [ebx + Data_BCD]
mov AH,AL
; Выделить, перевести в ASCII и
; сохранить старшую тетраду
shr AL,4
add AL,'0'
stosb
dec DX
jnz .N0
mov AL,'.'
stosb
.N0: ; Выделить, перевести в ASCII и
; сохранить младшую тетраду
mov AL,AH
and AL,0Fh
add AL,'0'
stosb
dec DX
jnz .N1
mov AL,'.'
stosb
.N1:
dec BX
loop .NextPair
mov AL,0
stosb
; Убрать незначащие нули слева
mov EDI, Data_String
mov ESI, Data_String
; Пропустить знак числа, если он есть
cmp byte [ESI],'-'
jne .N2
inc ESI
inc EDI
.N2: ; Загрузить в счетчик цикла количество разрядов
; числа плюс 1 (байт десятичной точки)
mov ecx,18+1+1
; Пропустить незначащие нули
.N3:
cmp byte [ESI],'0'
jne .N4
cmp byte [ESI+1],'.'
je .N4
inc ESI
loop .N3
; Ошибка - нет значащих цифр
jmp .Error
; Скопировать значащую часть числа в начало строки
align 4
.N4: rep movsb
jmp .End
; Ошибка
align 4
.Error:
mov AL,'E'
stosb
mov AL,'R'
stosb
mov AL,'R'
stosb
xor AL,AL
stosb
jmp .End
; Переполнение разрядной сетки
align 4
.Overflow:
mov AL,'#'
stosb
xor AL,AL
stosb
; Конец процедуры
align 4
.End:
popad
ret
;****************************************************
;* ПРЕОБРАЗОВАТЬ СТРОКУ В ЧИСЛО С ПЛАВАЮЩЕЙ ЗАПЯТОЙ *
;* (число имеет обычный, "бытовой" формат) *
;* Входные параметры: *
;* Data_String - число в коде ASCII. *
;* Выходные параметры: *
;* Data_Double - число в двоичном коде. *
;****************************************************
align 4
String_to_DoubleFloat:
pushad
cld
; Очищаем Data_BCD
mov dword [Data_BCD],0
mov dword [Data_BCD+4],0
mov word [Data_BCD+8],0
; Очищаем байт знака
mov [Data_Sign],0
; Заносим в esi указатель на строку
mov esi, Data_String
; Пропускаем пробелы перед числом
mov ecx,64 ;защита от зацикливания
.ShiftIgnore:
lodsb
cmp al,' '
jne .ShiftIgnoreEnd
loop .ShiftIgnore
jmp .Error
align 4
.ShiftIgnoreEnd:
; Проверяем знак числа
cmp al,'-'
jne .Positive
mov [Data_Sign],80h
lodsb
.Positive:
mov [Data_Flag],0 ;признак наличия точки
xor edx,edx ;позиция точки
mov ecx,18 ;макс. число разрядов
align 4
.ASCIItoBCDConversion:
cmp al,'.' ;точка?
jne .NotDot
cmp [Data_Flag],0 ;точка не встречалась?
jne .Error ;если точка уже была
mov [Data_Flag],1
lodsb
or al,al ;конец строки?
jnz .NotDot
jmp .ASCIItoBCDConversionEnd
align 4
.NotDot:
; Увеличить на 1 значение позиции точки,
; если она еще не встречалась
cmp [Data_Flag],0
jnz .Figures
inc edx
.Figures:
cmp al,'e'
je .exp_form
cmp al,'E'
jne @f
.exp_form:
call string_ExpForm ;если число в формате ..e..
or al,al
jnz .Error
jmp .ASCIItoBCDConversionEnd
@@:
; Символы числа должны быть цифрами
cmp al,'0'
jb .Error
cmp al,'9'
ja .Error
; Пишем очередную цифру в младшую тетраду BCD
and al,15 ;символы 0-9 переводим в число
or byte [Data_BCD],al
; Проверка на конец строки
cmp byte [esi],0
je .ASCIItoBCDConversionEnd
; Сдвигаем BCD на 4 разряда влево
; (сдвигаем старшие 2 байта)
mov ax,word [Data_BCD+6]
shld word [Data_BCD+8],ax,4
; (сдвигаем средние 4 байта)
mov eax,dword [Data_BCD]
shld dword [Data_BCD+4],eax,4
; (сдвигаем младшие 4 байта)
shl dword [Data_BCD],4
; Загружаем следующий символ в AL
lodsb
loop .ASCIItoBCDConversion ;если не компил. то поставить dec ecx, jnz ...
; Если 19-й символ не 0 и не точка,
; то ошибка переполнения
cmp al,'.'
jne .NotDot2
inc ecx ;пропуск точки в конце очень большого числа
lodsb
.NotDot2:
or al,al ;переполнение разрядной сетки?
jz .ASCIItoBCDConversionEnd
align 4
.Error: ; При любой ошибке обнулить результат
fldz ;занести ноль с стек сопроцессора
fstp [Data_Double]
jmp .End
; ПРЕОБРАЗОВАТЬ ЧИСЛО ИЗ КОДА BCD В ВЕЩЕСТВЕННОЕ ЧИСЛО
.ASCIItoBCDConversionEnd:
; Вписать знак в старший байт
mov al,[Data_Sign]
mov byte [Data_BCD+9],al
; Сбросить регистры сопроцессора
fninit
; Загрузить в сопроцессор число в BCD-формате
fbld [Data_BCD]
; Вычислить номер делителя или множителя
lea ebx,[ecx+edx-18]
cmp ebx,0
jle .NoMul ;если число e-..
dec ebx
jz .NoDiv ;если число e+0
dec ebx
lea ebx,[MConst+8*ebx]
cmp ebx,MConst.end
jl @f
ffree st0
fincstp
jmp .Error ;если очень большое число e+**
@@:
fmul qword [ebx] ;умножить на константу (для чисел с приставкой e+..)
jmp .NoDiv
.NoMul:
neg ebx
lea ebx,[MConst+8*ebx]
cmp ebx,MConst.end
jl @f
ffree st0
fincstp
jmp .Error ;если очень маленькое число e-**
@@:
fdiv qword [ebx] ;разделить на константу
.NoDiv: ;Выгрузить число в двоичном формате
fstp [Data_Double]
.End:
popad
ret
;output:
; eax - 1 if error
; edx += size
align 4
proc string_ExpForm uses ebx
mov [Data_Sign_Exp],0
xor eax,eax
lodsb
cmp al,'+'
jne @f
lodsb
@@:
cmp al,'-'
jne @f
inc [Data_Sign_Exp]
lodsb
@@:
xor ebx,ebx
.cycle0:
cmp al,0
je .cycle0end
cmp al,9
je .cycle0end
cmp al,10
je .cycle0end
cmp al,13
je .cycle0end
cmp al,' '
je .cycle0end
cmp al,'0'
jb .Error
cmp al,'9'
ja .Error
imul ebx,10
and eax,15 ;символы 0-9 переводим в число
add ebx,eax
lodsb
jmp .cycle0
.cycle0end:
cmp ebx,328 ;308 - макс. размер степени для double + 20 - число разрядов в BCD
ja .Error
cmp [Data_Sign_Exp],0
je @f
neg ebx
@@:
cmp [Data_Flag],0 ;точка не встречалась?
jne @f
dec edx
@@:
add edx,ebx
xor eax,eax
jmp @f
.Error:
xor eax,eax
inc eax
@@:
ret
endp
align 4
proc str_cat uses eax ecx edi esi, str1:dword, str2:dword
mov esi,dword[str2]
stdcall str_len,esi
mov ecx,eax
inc ecx
mov edi,dword[str1]
stdcall str_len,edi
add edi,eax
cld
repne movsb
ret
endp
;output:
; eax = strlen
align 4
proc str_len, str1:dword
mov eax,[str1]
@@:
cmp byte[eax],0
je @f
inc eax
jmp @b
@@:
sub eax,[str1]
ret
endp
align 4
proc String_crop_0 uses eax ebx ecx edi
mov edi,Data_String
mov al,'.'
mov ecx,32
repne scasb
mov ebx,edi
mov edi,Data_String
xor al,al
mov ecx,32
repne scasb
cmp ebx,edi
jg .end_f
dec edi
.cycle0:
dec edi
cmp edi,Data_String
jle .end_f
cmp byte[edi],'0'
jne .cycle0end
mov byte[edi],0
jmp .cycle0
.cycle0end:
cmp byte[edi],'.'
jne .end_f
mov byte[edi],0
.end_f:
ret
endp