Tvůrce webu je i pro tebe! Postav třeba web. Bez grafika. Bez kodéra. Hned.
wz

Obsah / Utility / TEXTFORM / FltToTextBufN

Zdrojový kód: INCLUDE\UTIL\TEXTFORM.INC, UTIL\TEXTFORM.ASM

Související:

FltToTextBuf   Zformátování desetinného čísla do bufferu
MixToTextBufN   Délka textu formátovaného smíšeného desetinného čísla
FormToTextBufN   Délka formátovaného textu

FltToTextBufN - Délka textu formátovaného desetinného čísla

Funkce FltToTextBufN zjistí délku formátovaného desetinného čísla v bufferu.


; -----------------------------------------------------------------------------
;  Convert float number into text buffer in float point form - get text length
; -----------------------------------------------------------------------------
; INPUT:	ST0 = float number (it does not pop it from the FPU stack)
;		EBX = formatting parameters FORMPAR
; OUTPUT:	EAX = text length
; NOTES:	It uses 1 more register from FPU stack.
;		To destroy FPU register you can use "ffreep st0" instruction.
; -----------------------------------------------------------------------------
; Local variables:
%define		FLTNForm   ebp-4	; (4) formatting parameters FORMPAR
					; (1)
%define		FLTNSign   ebp-6	; (1) stored flags, bit 7 = sign
%define		FLTNExp    ebp-8	; (2) float number, exponent
					;       (bit 0..14) and sign (bit 15)
%define		FLTNMantH  ebp-12	; (4) float number, mantissa HIGH
%define		FLTNMantL  ebp-16	; (4) float number, mantissa LOW
%define		FLTNFloat  FLTNMantL	; ...(10) float number

%define		FLTNStack   16		; stack size

Na vstupu funkce obsahuje registr ST0 desetinné číslo ke konverzi a registr EBX formátovací parametry FORMPAR (ne ukazatel). Na výstupu je navrácena v registru EAX délka textu. Funkce používá 1 další registr koprocesoru. Vstupní číslo v registru ST0 zůstává zachováno, neuvolňuje se ze zásobníku koprocesoru. K uvolnění čísla z registru ST0 lze použít instrukci "ffreep st0" volanou po ukončení funkce. Funkce předpokládá, že koprocesor je v implicitním nastavení - přesnost 64 bitů, zaokrouhlení k nejbližšímu.


; ------------- Push registers

FltToTextBufN: 	push	ebx		; push EBX
		push	ecx		; push ECX
		push	edx		; push EDX
		push	ebp		; push EBP
		mov	ebp,esp		; EBP <- push ESP
		sub	esp,FLTNStack	; ESP <- create local variables

; ------------- Prepare local variables

		mov	[FLTNForm],ebx	; formatting parameters FORMPAR

Po úschově obsahu registrů je v zásobníku vytvořeno volné místo pro uložení čísla v rozšířené přesnosti a formátovací parametry jsou uloženy do proměnné FLTNForm.


; ------------- Store number into stack

		fld	st0		; duplicate number
		fstp	tword [FLTNFloat] ; store number into stack

; ------------- Load exponent (-> EDX)

		fwait			; synchronize FPU
		movzx	edx,word [FLTNExp] ; EDX <- exponent
		mov	ch,dh		; CH <- sign
		and	ch,B7		; left only sign flag
		mov	[FLTNSign],ch	; push sign flag (bit 7)

; ------------- Absolute value (it uses copy of register)

		fld	st0		; duplicate number
		fabs			; absolute value
		and	dx,7fffh	; mask exponent (15 bits)
		jnz	FltToTextBufN2	; neither zero nor denormalized number

Číslo se uloží do bufferu v zásobníku (nejdříve se musí zduplikovat, protože není k dispozici instrukce pro uložení čísla v rozšířené přesnosti bez uvolnění čísla ze zásobníku koprocesoru). Do registru EDX se načte exponent čísla. Příznak záporného čísla (bit 15 exponentu) se uloží do proměnné FLTNSign. Číslo se převede na absolutní hodnotu. Vynulováním nejvyššího bitu v registru DX se vynuluje znaménkový bit. Je-li exponent v registru DX nulový, jedná se o speciální případ nuly nebo nenormalizovaného čísla.


; ============= Special cases

; ------------- Zero or denormalized number

		mov	eax,[FLTNMantL]	; EAX <- mantissa LOW
		or	eax,[FLTNMantH]	; check if mantissa is zero
		jnz	FltToTextBufN2	; not zero, it is denormalized number

; ------------- Zero

		ffreep	st0		; pop register from the FPU stack
		xor	ebx,ebx		; EBX <- zero decimal exponent
	      test byte [FLTNForm+FORMPAR_Flags2],FORMFLAG2_Alt2+FORMFLAG2_Prec
		jz	FltToTextBufN6	; don't truncate
FltToTextBufN1:	xor	ecx,ecx		; limit precision
		jmp	FltToTextBufN61

V případě nulového exponentu se provede text mantisy. Není-li mantisa nulová, jedná se o nenormalizované číslo (s exponentem menším než přibližně 1e-4931), takové číslo se bude dále zpracovávat běžným způsobem.

Jsou-li exponent i mantisa nulové, jedná se o nulu. Číslo se uvolní z registru koprocesoru a dále se pokračuje obsluhou se zkrácením koncových nul nebo bez zkrácení nul.


; ------------- Infinity (+1.#INF, -1.#INF) or non-number (+1.#NAN, -1.#NAN)

FltToTextBufN2:	cmp	dx,7fffh	; infinity or non-number?
		jne	FltToTextBufN3	; neither infinity nor non-number
		ffreep	st0		; pop register from the FPU stack
		xor	ecx,ecx		; ECX <- 0
		mov	cl,7		; ECX <- 7, text length
		jmp	FltToTextBufN8

Má-li exponent v registru DX hodnotu 7FFFh, jedná se o další zvláštní případ - nekonečno nebo nedefinované číslo. Číslo se uvolní ze zásobníku a do registru ECX se připraví délka textu (=7), funkce poté přejde přímo na obsluhu minimální šířky pole s textem.


; ------------- Split number to mantissa and exponent

FltToTextBufN3:	lea	ecx,[FLTNFloat]	; ECX <- pointer to float in stack
		call	FloatSplit	; split number to mantissa and exponent

Pomocí funkce FloatSplit je číslo rozloženo na mantisu a dekadický exponent. Na vstupu funkce je v registru EDX binární exponent čísla, registr ECX ukazuje na buffer čísla v zásobníku a v registru ST0 je absolutní hodnota konvertovaného čísla. Funkce navrací v registru EBX dekadický exponent a v registru ST0 mantisu čísla (normalizovanou do rozsahu 1 včetně až 10 vyjma).


;-------------- Round, denormalize and convert mantissa to BCD (EBX = exponent)

		movzx	eax,byte [FLTNForm+FORMPAR_Prec] ; EAX <- precision
		test	byte [FLTNForm+FORMPAR_Flags2],FORMFLAG2_Prec; precis.?
		jz	FltToTextBufN4	; normal precision (=fractional digits)
		dec	eax		; without first digit
		jns	FltToTextBufN42	; digits are OK
		jmp	FltToTextBufN41	; limit to 0
FltToTextBufN4:	add	eax,ebx		; EAX <- position of last digit
		jg	FltToTextBufN42	; digits are OK
FltToTextBufN41:xor	eax,eax		; EAX <- 0, digits are out of display
FltToTextBufN42:lea	ecx,[FLTNFloat]	; ECX <- pointer to float in stack
		bt	dword [FLTNForm],FORMTYPE_Cap_b ; round always down?
		call	FloatMantBCD	; process mantissa

Číslo bude pomocí funkce FloatMantBCD zaokrouhleno na poslední číslici a rozloženo na jednotlivé číslice. Před voláním funkce je do registru EAX připravena požadovaná přesnost.

Není-li nastaven příznak FORMFLAG2_Prec, má přesnost význam počtu číslic desetinné části. Přičtením exponentu se obdrží počet zobrazených platných číslic - 1. Podteče-li počet číslic pod 0, číslo leží celé za koncem zobrazené desetinné části, počet číslic se omezí na 0.

Je-li zvolena alternativní přesnost FORMFLAG2_Prec, má přesnost význam počtu významných číslic. Odečtením 1 se provede korekce pro funkci FloatMantBCD (která očekává počet číslic bez první číslice), došlo-li podtečení pod 0, omezí se počet číslic na 0.

Do registru ECX se připraví ukazatel na buffer čísla v zásobníku. Testem příznaku FORMTYPE_Cap_b je funkci předán příznak, zda se má vypnout zaokrouhlování poslední zobrazené číslice. Voláním FloatMantBCD se mantisa převede na BCD číslice.


; ------------- Use precision digits (here is EBX = decimal exponent)

		lea	edx,[FLTNFloat+9] ; EDX <- last digit
		test	byte [FLTNForm+FORMPAR_Flags2],FORMFLAG2_Prec; precis.?
		jz	FltToTextBufN43	; normal precision (=fractional digits)
		call	FloatTrunc	; truncate trailing zeros
		sub	ecx,ebx		; ECX <- maximal precision
		jg	FltToTextBufN61	; precision is positive
		xor	ecx,ecx		; ECX <- limit precision
		jmp	FltToTextBufN61

; ------------- Truncate trailing zeros (here is EBX = decimal exponent)

FltToTextBufN43:test	byte [FLTNForm+FORMPAR_Flags2],FORMFLAG2_Alt2 ; trunc.?
		jz	FltToTextBufN6	; don't truncate
		movzx	eax,byte [FLTNForm+FORMPAR_Prec] ; EAX <- precision
		add	eax,ebx		; check if number is out of display
		jl	FltToTextBufN1	; number is not visible
		call	FloatTrunc	; truncate trailing zeros
		sub	ecx,ebx		; ECX <- maximal precision
		jg	FltToTextBufN44	; precision is positive
		xor	ecx,ecx		; ECX <- limit precision
FltToTextBufN44:movzx	eax,byte [FLTNForm+FORMPAR_Prec] ; EAX <- precision
		cmp	ecx,eax		; check precision
		jae	FltToTextBufN6	; precision is OK
		mov	[FLTNForm+FORMPAR_Prec],cl ; limit precision

Bude následovat ořezání nevýznamných koncových nul (s využitím funkce FloatTrunc). Do registru EDX se připraví ukazatel na poslední bajt mantisy v BCD tvaru a rozliší se, jakým způsobem má být interpretována požadovaná přesnost.

Je-li nastaven příznakový bit FORMFLAG2_Prec, má přesnost význam počtu významných číslic. Přesnost navrácená funkcí FloatTrunc (tedy počet významných číslic - 1) se použije jako nová požadovaná přesnost k zápisu čísla, tj. počet dekódovaných významných číslic - 1. Odečtením exponentu v registru EBX se obdrží počet číslic desetinné části. Ponechá se v registru ECX a při podtečení pod nulu (tj. číslo je příliš vlevo) se omezí na 0.

Není-li příznakový bit FORMFLAG2_Prec nastaven, má přesnost význam počtu desetinných míst. Není-li požadováno odstranění nevýznamných koncových nul, následující úsek se přeskočí. Jinak se pokračuje přípravou požadované přesnosti do registru EAX. Přičtením exponentu z registru EBX se zjistí vzdálenost první významné číslice od poslední číslice desetinné části. Podteče-li vzdálenost pod nulu, leží číslo za koncem desetinné části a tedy se nezobrazí. V tom případě se nahradí číslem nula bez desetinných míst.

Zavoláním funkce FloatTrunc se zjistí počet významných číslic - 1 (registr ECX). Odečtením exponentu z registru EBX se obdrží skutečný počet číslic desetinné části. V případě podtečení, tj. leží-li číslo příliš vlevo, se omezí na nulu. Do registru EAX se načte požadovaná přesnost a porovná se se skutečnou přesností. Je-li skutečná přesnost nižší, nahradí se jí požadovaná přesnost v proměnné formátovacích parametrů.


; ============= Get text length (here is EBX = decimal exponent)

; ------------- Add sign (-> EAX)

FltToTextBufN6:	movzx	ecx,byte [FLTNForm+FORMPAR_Prec] ; ECX<-num. of digits
FltToTextBufN61:mov	al,[FLTNSign]	; AL <- sign (bit 7)
		mov	ah,[FLTNForm+FORMPAR_Flags1] ; AH <- flags 1
		and	ax,B7 + (FORMFLAG1_Sign + FORMFLAG1_Space)*256
		setnz	al		; AL <- 1 if sign, else 0
		movzx	eax,al		; EAX <- 1 if sign, else 0

; ------------- Add decimal point and precision (-> ECX)

		jecxz	FltToTextBufN62	; no digits after decimal point
		bts	dword [FLTNForm],FORMFLAG_Alt_b ; set dec. point flag
FltToTextBufN62:bt	dword [FLTNForm],FORMFLAG_Alt_b ; use decimal point?
		adc	ecx,eax		; ECX<-add decimal point and sign

V další části bude zjištěna délka textu dekódovaného čísla. Do registru ECX se připraví požadovaná přesnost představující počet číslic za desetinnou tečkou. V případě alternativní přesnosti se tato instrukce přeskočí s registrem ECX již připraveným.

Do registru AL se připraví znaménkový bit, do registru AH formátovací příznaky. Je-li znaménkový bit nastaven (bude znaménko "-") nebo je-li nastaven požadavek vynuceného znaménka (bude znaménko "+") nebo je-li nastaven požadavek vynucené mezery, připraví se do registru EAX číslo 1 jako délka textu znaménka, jinak bude obsah registru EAX nulový.

Není-li počet číslic desetinné části nulový, nastaví se vynuceně příznak požadavku uložení oddělovače desetinných míst. Je-li příznak FORMFLAG_Alt_b nastaven, je požadováno uložení oddělovače desetinných míst, délka textu ve střadači ECX se zvýší o 1. Současně se přičte délka textu znaménka (která může být 0 nebo 1).


; ------------- Prepare minimal integer part (-> EAX)

		xor	eax,eax		; EAX <- 0
		or	ebx,ebx		; is exponent negative?
		sets	al		; AL <- 1 if < 1.0, 0 if >= 1.0
		dec	eax		; EAX <- 0 if < 1.0, -1 if >= 1.0
		and	eax,ebx		; EAX <- 0 if < 1.0, exponent if >= 1.0
		inc	eax		; EAX <- add first digit

Délka celočíselné části odpovídá kladné hodnotě exponentu zvýšené o 1. V případě záporného exponentu bude hodnota exponentu omezena na 0. Namísto testu se skokovou instrukcí se použije rychlejší registrová operace. Testem exponentu v registru EBX se zjistí, zda je exponent záporný. Pokud ano, nastavi se obsah registru AL na 1. Pokud ne, bude obsah registru AL nulový.

Dekrementací EAX se připraví do EAX maska, která je nulová v případě záporného exponentu, v případě nezáporného exponentu je 0FFFFFFFFh. Zamaskováním s exponentem v registru EBX se v registru EAX obdrží hodnota exponentu omezená na nulu při záporném exponentu. Po inkrementaci údaje bude registr EAX obsahovat počet číslic celočíselné části.


; ------------- Check if use zeros instead of spaces

		test	byte [FLTNForm+FORMPAR_Flags2],FORMFLAG2_Zero ; zero?
		jz	FltToTextBufN64	; no zeros

; ------------- Prepare minimal integer part if use zeros (-> EDX)

		movzx	edx,byte [FLTNForm+FORMPAR_Width] ; EDX<-minimal width
		sub	edx,ecx		; EDX <- without sign and fractional
		jle	FltToTextBufN64	; not enough space

; ------------- Check if use thousand separator

		test	byte [FLTNForm+FORMPAR_Flags2],FORMFLAG2_Thsnd ; thsnd?
		jz	FltToTextBufN63	; not using thousand separator

; ------------- Number of digits without thousand separators (-> EDX)

		inc	edx		; EDX <- width + 1
		imul	edx,edx,3	; multiply with 3
		shr	edx,2		; EDX <- /4, max. number of digits

; ------------- New minimal number of integer digits (-> EAX)

FltToTextBufN63:cmp	eax,edx		; check minimal number of digits
		ja	FltToTextBufN64	; number of digits is OK
		xchg	eax,edx		; EAX <- new number of digits

Není-li nastaven příznak FORMFLAG2_Zero, výpočet počtu číslic celočíselné části je hotov a může se přejít k dalšímu vyhodnocování. Jinak se pokračuje zjištením minimálního počtu číslic vyplývajícího z požadované minimální šířky pole s číslem.

Do registru EDX je načtena požadovaná minimální šířka pole s číslem. Po odečtení délky textu desetinné částí se znaménkem v registru ECX se obdrží zbývající šířka pro celočíselnou část. Dál má smysl pokračovat jen je-li zbývající šířka kladná.

Není-li nastaven příznakový bit FORMFLAG2_Thsnd, nepoužijí se oddělovače řádů a registr EDX již přímo obsahuje počet číslic. Pokud se oddělovače řádů mají použít, je nutno odečíst znaky připadající na oddělovače řádů. Inkrementací šířky pole v registru EDX se provede korekce pro započtení první číslice. Vynásobením číslem 3/4 se v registru EDX obdrží počet číslic bez oddělovačů řádů.

Po výpočtu minimálního počtu číslic celočíselné části, vyžadovaného doplněním nul zleva, se minimální počet číslic porovná se skutečně zjištěným počtem číslic v registru EAX a je-li větší, použije se namísto skutečného počtu číslic.


; ------------- Add thousand separators (-> ECX)

FltToTextBufN64:test	byte [FLTNForm+FORMPAR_Flags2],FORMFLAG2_Thsnd ; thsnd?
		jz	FltToTextBufN66	; not using thousand separator
		lea	edx,[eax-1]	; EDX <- number of digits - 1
		imul	edx,edx,43691	; EDX <- (digits-1) * 20000h/3
		shr	edx,17		; EDX <- number of separators
		add	eax,edx		; EAX <- add thousand separators
FltToTextBufN66:add	ecx,eax		; EAX <- add sign and fractional part

Dále je potřeba k délce textu přičíst oddělovače řádů. Nejsou-li oddělovače řádů požadovány (tj. není nastaven příznak FORMFLAG2_Thsnd), bude délka textu odpovídat součtu délky celočíselné části a desetinné části se znaménkem a oddělovačem destinných míst. Jsou-li oddělovače řádů požadovány, je potřeba zjistit jejich počet z počtu číslic celočíselné části.

Do registru EDX se připraví počet číslic celočíselné části snížený o první číslici, která je vždy vyžadována před prvním oddělovačem. Vydělením počtu číslic třemi se obdrží počet celých skupin číslic po 3 znacích, které budou odpovídat počtu oddělovačů řádů. Namísto operace dělení se použije rychlejší operace násobení převrácenou hodnotou. Výsledek se obdrží v registru EDX a přičtením k registru EAX se opraví údaj délky celočíselné části.


; ------------- Check field width

FltToTextBufN8:	movzx	eax,byte [FLTNForm+FORMPAR_Width] ; EAX<-minimal width
		cmp	eax,ecx		; check minimal width
		ja	FltToTextBufN84	; use minimal width
		xchg	eax,ecx		; EAX <- text length

; ------------- Pop registers

FltToTextBufN84:mov	esp,ebp		; pop ESP
		pop	ebp		; pop EBP
		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	ebx		; pop EBX
FltToTextBufN9:	ret

Na závěr se porovná zjištěná délka textu čísla s požadovanou minimální šířkou pole s textem a je-li menší, navrátí se namísto zjištěné délky textu šířka pole s textem. Po navrácení ukazatele zásobníku se navrátí uschované registry.


Obsah / Utility / TEXTFORM / FltToTextBufN