· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.A&O —› -= Float_to_Str =- (алгоритм 5)

. 1 . 2 . 3 . >>

Посл.отвђт Сообщенiе


Дата: Сен 16, 2003 16:16:08

Вот, наконец.. только что с отладчика!!!
Мой алгоритм домножения на 5...

Код глючный, но работает :)))
Правда -- экспонента пока не переводится..
И в конце функция пока не верно завершена...
Код непричёсан.
Но пока интересны идеи по оптимизации
;; ##################################################################
;; FUN::Float_to_String
;;------------------------------
; CONV::ASMCALL
; FORMALS::
;	st(0)	= reg	type:real10
;			- Число подлежащее преобразованию
;	edx		= reg	type:pointer
;			- Указатель на строку, которая будет выведена
; DPN::
; Описание функции
;------------------------------
; ALG:
Comment # Алгоритм
		-= Преобразование числа в строку =-

1. Находим 10 экспоненту числа.
	Для того, чтобы это сделать достаточно
	получить логорифм числа по основанию 10.

2. Приводим степень до целого числа.
	Получаем число 10^степень

3. Делим исходное число на 10-ю экспоненту

4. Отделяем дробную от целой части.

5. Переводим целую часть (как Int_To_Str) в строку

6. Переводим дробную часть в строку (Алгоритм *5)

	6.1 Дробрая часть имеет вид нормализованного числа
		(64 bit + биты на переполнение)
		При чём старший бит находится в 64-ой позиции (отсчёт от 0)

	6.2 Число (64-bits+1) умножается на 5

	6.3 Всё, что попало далее 63-bit - и есть первое число после запятой

	6.4 В 63 битах находится оставшееся число

	6.4 Мы подвигаем его на 1 бит.

		6.4.1 Если старший бит не 1, значит следующий десятичный 
				разряд = 0
		6.4.2 Иначе, повторяем с 6.2

	6.5 Цикл окончен, если число в 63 битах = 0.

7. Выводим экспоненту

#
;; ==========================================================
Float_to_String	proc
;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Comment #
#
; USE:
; eax,edx,ecx,edi
;; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;; Создание стекового фрейма, таким, чтобы он был выровнен
;; на границу параграфа 16 байт.
		mov		eax,esp
		sub		esp,14+12+2		;; (14 для переменных + 12 для хранения регистров)
		and		esp,0fffffff0h
;; Сохраним действительное 
		mov		[esp+16],eax	
;; Значение esp
		mov		[esp+20],esi	;; Значения регистров, которые не изменяются 
		mov		[esp+24],ebx	;;

;; Определение стековых констант
$__real10		EQU	[esp]
$__fpumode_1	EQU	[esp+10]
$__fpumode		EQU	[esp+12]
;; Установка режима точности вычислений 80-bits
		fnstcw	word ptr $__fpumode
;; Создание копии для восстановления режима.
		fnstcw	word ptr $__fpumode_1
		mov		byte ptr $__fpumode+1,0011b
		fldcw	word ptr $__fpumode

;; 1. Вычисляем степень 10
;; log10(x) = log10(2)*log2(x) = FYL2X(FLDLG2,x)

		fst		st(1)		;; Копия исходного числа
		fldlg2
		fxch	st(1)
		fyl2x
		
;; 2. Теперь когда мы получили результат
;; Нам нужно извлеч из него только целую часть
;; Для этого нужно поменяв режим округления, округлить
;; результат

;; Режим округления к нулю
		mov	byte ptr $__fpumode+1,1111b
		fldcw	word ptr $__fpumode
		fistp	dword ptr $__real10
;;		fstp	st(0)			;; Удаляем значение log2(10)
;; Восстанавливаем режим округления
		mov	byte ptr $__fpumode+1,0011b
		fldcw	word ptr $__fpumode
;; 3. Теперь нужно получить экспоненту 10 - 10^st(0)
		mov		eax,dword ptr $__real10
;; Нужно сохранить это число для сравнения его на знак
		mov		ecx,eax
		call		_exp10
;; 4. Деление на экспоненту
;; Теперь в st(0) то число, которое нам нужно
;; Мы должны:
;; Если число отрицательное умножить на него st(1)
;; Иначе, разделить
@@:
;; if ecx < 0
		neg	ecx
		js	short @F		;; Если знака не было.
;; then 
		;; Иначе знак был
		fmulp	st(1),st(0)
		jmp	next5
@@:
;; else
		fdivp	st(1),st(0)
;; endif
next5:
;; 5. После этого в st(0) оказывается нужное нам число
;; При чём это число оказывается в формате 
;; x.xxxxxxx где x число от 0 до 9
;; именно его и следует переводить в строку.
;; Для этого число раздляется на дробную и целую части, которые конвертируются
;; отдельно
;; !!!
;; Заметте, что в ecx хранится число с обратным знаком
;; знаку числа степени 10.

;; 5.1 Разбиваем число на дробную и целую части
		mov	byte ptr $__fpumode+1,1111b
		fldcw	word ptr $__fpumode
		fld	st(0)
		frndint
		fsub	st(1),st(0)
		fistp	dword ptr $__real10
		mov	eax,$__real10
		fstp	tbyte ptr $__real10
;; 5.2 В регистре eax хранится целая часть. Это одна цифра, а поэтому
		neg		eax
		js short	@F		;; Если знака не было.
		jz short    @F		;; Если ноль
		;;Если знак был
		add		eax,'.' shl 8 	;;(знак точки)
		shl		eax,8
		add		eax,'-'
		mov		[edi],eax
		add		edi,3		;; Мы записали в строку 3 байта
		jmp	short next5_3
@@:
		;; Иначе в eax было беззнаковое число
		neg		al
		mov		byte ptr [edi], al
		mov		byte ptr [edi+1], '.'		
		add		edi,2
next5_3:
		push	ecx
;; 5.3 На этом этапе должно произойти преобразование числа
;; Это преобразование основано на переполнении 64-bit-ного числа
;; которое находится по адресу esp+4 (потому что ecx)
$__int64		EQU	[esp+4]
$__exponent		EQU [esp+4+8]

;;----------- Преобразование мантиссы ------------------------
;;Это преобразование было бы намного проще, если 
;;экспонента не содержала значение до -3
;;А его нужно учитывать при переводе.
;;Поэтому вместо перемножения 64-bits числа 
;;Мы перемножаем 96-bits

;; 0) Получим экспоненту в понятном нам виде
		movsx	ecx,word ptr $__exponent
		mov		eax,16383-1
		sub		ecx,eax
;; Преобразовываем знак экспоненты, поскольку она всегда меньше 0
		neg		ecx
;; 0*) Выполняем смещение всего числа в право на cl бит
		xor		esi,esi
		mov		edx,$__int64
		shrd	esi,edx,cl
		mov		eax,$__int64+4
		shrd	edx,eax,cl
		shr		eax,cl
		mov		$__int64,edx
		mov		$__int64+4,eax

;; 1) Умножить на 5 младшую половину 64-bits числа

;; Инициализируем счётчик точности
		mov		eax,esi
		mov		ecx,20

;;++++++++++++++++++ Цикл ++++++++++++++++++++++++++++++

floatloop:

		mul		CONSTANT_5
;; 2) Запомнить младшую часть результата
		mov		esi,eax
		mov		ebx,edx
;; 3) Перемножить старшее слово
		mov		eax,$__int64
		mul		CONSTANT_5
		add		eax,ebx
		adc		edx,0
		mov		$__int64,eax
		mov		ebx,edx
;; И ещё одно старшее слово
		mov		eax,$__int64+4
		mul		CONSTANT_5
		add		eax,ebx
		adc		edx,0
;;		mov		$__int64+4,eax
;; 4) После этой команды в регистре edx (+ старший бит eax)
;; лежит то самое число
;; которое необходимо поместить в строку
;; Эти несколько команд так же осуществляют сдвиг
;; 96-bits числа на 1 бит в лево.
		shld	edx,eax,1
		mov		$__int64+4,eax
		mov		byte ptr [edi],dl	;; Помещаем символ в строку
		mov		eax,$__int64
		shld	$__int64+4,eax,1
		inc		edi
		shld	eax,esi,1
		shl		esi,1
		mov		$__int64,eax
		dec		ecx
		mov		eax,esi
		jnz	short floatloop


;;----------- Преобразование мантиссы (Конец) ----------------

;;----------- Преобразование экспоненты ecx ------------------
		pop		eax
;; Сделать
error:
		pop		eax
		fldcw	word ptr [esp+2]
		pop		eax
		xor		eax,eax
		ret		
error_exp:
		pop		eax
		jmp		error
Float_to_String	endp
;; ##########################################################


Дата: Сен 16, 2003 16:59:08

Edmond
;; 1. Вычисляем степень 10
;; log10(x) = log10(2)*log2(x) = FYL2X(FLDLG2,x)


а нас в школе учили вот так:
log10(x) = log2(x)/log2(10)

забавно получается, log2(10) = 1/log10(2) :))


Дата: Сен 16, 2003 17:33:23

DaemoniacaL
Гм.. :)))
И меня учили :))
Надо посмотреть, в чём я ошибся. Но код работает верно.


Дата: Сен 16, 2003 17:36:20

Edmond
Переведи этим алгоритмом число 10^4000 в строку и посмотри что получится.


Дата: Сен 16, 2003 17:43:12

Edmond
Ошибки там нет, log2(10) действительно равен 1/log10(2)


Дата: Сен 16, 2003 17:47:20

Black_mirror
А что разве в регистре ecx не оказывается 4000?
ОК. Я проверю.


Дата: Сен 16, 2003 17:49:04

Black_mirror
Я и говорю забавно, сам проверил :) Думал ошибку у Edmond'a нашел... ан нет... хех :)


Дата: Сен 16, 2003 17:50:41

;; 3. Теперь нужно получить экспоненту 10 - 10^st(0)
mov eax,dword ptr $__real10
;; Нужно сохранить это число для сравнения его на знак
mov ecx,eax
call _exp10
;; 4. Деление на экспоненту


Вот тут работает ПРАВИЛЬНО!!!
Где я ошибся?


Дата: Сен 16, 2003 17:52:20

DaemoniacaL
Мдя.. Ничего не понимаю :(((
Только что посмотрел свою статью про FPU.. И там *!!!!
Неужели я ошибся в описании с командой fyl2x????


Дата: Сен 16, 2003 18:27:27

Edmond
дело в том что умножать надо на "обратный" логарифм.

т.е. если в общем виде:

log a (b) = log c (b) / log c (a)

то это эквивалентно

log a (b) = log c (b) * log a (c)

т.к. log a (b) = 1 / log b (a)


Дата: Сен 16, 2003 18:46:14

DaemoniacaL
Ну вот, сам запутал, сам распутал :)) Спасибо!!!


Дата: Сен 17, 2003 01:04:51

Edmond
Если процедура _exp10 использует функцию fx2m1 то точность будет плохая. Или у тебя это делается умножениями?


Дата: Сен 17, 2003 04:49:52

Edmond
Считать логарифм нуля или отрицательного числа бессмысленно, а проверки перед fyl2x отсутствуют.


js short @F ;; Если знака не было.
jz short @F ;; Если ноль

можно заменить на jle @F


;; 0) Получим экспоненту в понятном нам виде
movsx ecx,word ptr $__exponent

В самом старшем бите находится знак числа, а не экспоненты!

Можно обойтись без умножения 96и-битного числа:
Если привести мантисту к диапазону [1..10) то сдвигать ее придется не в право, а в лево. После первого сдвига мы получим целую часть в самом старшем слове и дробную в оставшихся 64х битах. Умножая в цикле дробную часть на 10 мы получим оставшиеся цифры.


Дата: Сен 17, 2003 12:57:13

Black_mirror
А как быть с периодическими дробями? Думаешь цифры закончатся после умножения на 2^64 ?


Дата: Сен 17, 2003 16:16:21

DaemoniacaL
После умножения на 10^64 они точно закончатся 8) Но выводит больше 20 цифр смысла все равно нет. У меня даже 20я цифра мусор. Нужно придумать как это правильно округлить.

. 1 . 2 . 3 . >>


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.066