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

Obsah / Utility / TEXTFORM / UIntToTextBufN

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

Související:

UIntToTextBuf   Zformátování čísla INT bez znaménka do bufferu
FormToTextBufN   Délka formátovaného textu

UIntToTextBufN - Délka textu formátovaného dekadického čísla bez znaménka

Funkce UIntToTextBufN zjistí délku formátovaného dekadického čísla bez znaménka v bufferu.


; -----------------------------------------------------------------------------
;          Convert unsigned INT number into text buffer - get text length
; -----------------------------------------------------------------------------
; INPUT:	EDX:EAX = number
;		EBX = formatting parameters FORMPAR
; OUTPUT:	EAX = text length
; -----------------------------------------------------------------------------

Na vstupu funkce obsahuje registrový pár EDX:EAX číslo bez znaménka ke konverzi a registr EBX formátovací parametry FORMPAR (ne ukazatel). Na výstupu je navrácena v registru EAX délka textu.


UIntToTextBufN:	call	UInt0ToTextBufN	; get text length without spaces
		push	ebx		; push EBX
		movzx	ebx,bh		; EBX <- minimal width of field
		cmp	ebx,eax		; check text length
		jb	UIntToTextBufN8	; text length is OK
		xchg	eax,ebx		; EAX <- new text length
UIntToTextBufN8:pop	ebx		; pop EBX
UIntToTextBufN9:ret

Funkce UIntToTextBufN volá interní funkci UInt0ToTextBufN, která nezapočítává požadovanou šířku pole. Porovnáním s požadovanou minimální šířkou se ověří, zda je délka textu čísla větší než požadovaná minimální šířka. Pokud je menší, navrátí funkce požadovanou minimální šířku namísto délky textu bez mezer.


; -----------------------------------------------------------------------------
; Convert unsigned INT number into text buffer - get text length without spaces
; -----------------------------------------------------------------------------
; INPUT:	EDX:EAX = number
;		EBX = formatting parameters FORMPAR
; OUTPUT:	EAX = text length
; -----------------------------------------------------------------------------

Funkce UInt0ToTextBufN zjistí délku textu bez uvažování minimální šířky textu. Na vstupu funkce obsahuje registrový pár EDX:EAX číslo bez znaménka ke konverzi a registr EBX formátovací parametry FORMPAR (ne ukazatel). Na výstupu je navrácena v registru EAX délka textu.


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

UInt0ToTextBufN:push	edx		; push EDX
		push	edi		; push EDI
		xchg	eax,edi		; EDI <- number LOW

Po úschově obsahu registrů EDX a EDI se uschová nižší část dekódovaného čísla do registru EDI.


; ------------- Check if number is QWORD

		or	edx,edx		; is number QWORD?
		jnz	UInt0ToTextBfN1	; number is QWORD

; ------------- Get number of bits in number LOW (-> AL)

		bsr	eax,edi		; AL <- highest bit in number LOW
		setz	ah		; AH <- 1 if EAX == 0, 0 if EAX != 0
		add	al,1		; AL <- number of bits in number LOW
		dec	ah		; AH <- 0 if EAX == 0, 0ffh if EAX != 0
		and	al,ah		; clear AL if number LOW is zero

; ------------- Get number of digits (-> EDX) (ln 10/ln 2 = 3.3219)

		mov	ah,78		; AH <- 100h / 3.3219 round up
		mul	ah		; AH <- aprox. number of digits
		movzx	edx,ah		; EDX <- aprox. number of digits
		cmp	[IntMul10+edx*4],edi ; check number LOW
		adc	dl,dh		; EDX <- number of digits
		jmp	UInt0ToTextBfN3

Testem registru EDX se rozliší, zda má číslo velikost DWORD (1 dvojslovo) nebo QWORD (2 dvojslova).

Má-li číslo rozměr DWORD, vyhledá se v registru EAX (nižší část čísla) nejvyšší pozice jedničkového bitu. Pokud byl obsah registru EAX nulový, nastaví se příznak ZF. To využije následující instrukce SETZ, která uloží do registru AH hodnotu 1 v případě nulového obsahu čísla, jinak do AH uloží 0. Přičtením 1 k AL se obdrží v registru AL počet bitů původního čísla LOW. Avšak pokud byl obsah čísla LOW nulový, je obsah registru AL nedefinovaný. Proto je AL zamaskován maskou vygenerovanou z příznaku ZF v registru AH. Výsledkem je, že registr AL obsahuje buď počet bitů v původní nižší části čísla nebo nulu pokud byla nižší část čísla nulová.

Počet bitů se přepočte na počet číslic a to tak, že se počet bitů nejdříve vynásobí poměrem dvojkového a dekadického logaritmu. Vznikne přibližný počet dekadických číslic, zaokrouhleno dolů, který se uloží do registru EDX. Stejně jako u funkce UDWToTextBufN se počet číslic upřesní pomocí tabulky IntMul10.


; ------------- Get number of bits in number HIGH (-> AL)

UInt0ToTextBfN1:bsr	eax,edx		; EAX <- highest bit in number HIGH
		add	al,32+1		; AL <- number of bits

; ------------- Get number of digits (-> EDX) (ln 10/ln 2 = 3.3219)

		mov	ah,78		; AH <- 100h / 3.3219 round up
		mul	ah		; AH <- aprox. number of digits
		movzx	eax,ah		; EAX <- aprox. number of digits
		sub	edi,[Int2Mul10+eax*8-10*8] ; check number LOW
		sbb	edx,[Int2Mul10+eax*8+4-10*8] ; check number HIGH
		cmc			; CY = number is not less
		adc	al,ah		; EAX <- number of digits
		xchg	eax,edx		; EDX <- number of digits

Má-li číslo rozměr QWORD (2 dvojslova), vyhledá se v registru EDX (vyšší část čísla) nejvyšší pozice jedničkového bitu. Obsah registru EDX nebyl nulový, proto není třeba obsluhovat případ nuly. Zvýšením pozice bitu o 32+1 bude registr AL obsahovat počet bitů čísla.

Zjištěný binární logaritmus bude převeden na dekadický logaritmus vydělením číslem ln 10 / ln 2 = 3,3219 (přibližně). Namísto dělení se použije rychlejší operace násobení převrácenou hodnotou, tj. číslem 100h / 3.3219 (zaokrouhleno nahoru). Po převzetí výsledku z registru AH se v registru EAX obdrží výsledný dekadický logaritmus (přesněji jeho celočíselná část).


; ------------- Table of multiple of 10 (1e10 to 1e19)

		align	8, db 0
Int2Mul10:	dd	540BE400h,2		; 1e10
		dd	4876E800h,17h		; 1e11
		dd	0D4A51000h,0E8h		; 1e12
		dd	4E72A000h,918h		; 1e13
		dd	107A4000h,5AF3h		; 1e14
		dd	0A4C68000h,38D7Eh	; 1e15
		dd	6FC10000h,2386F2h	; 1e16
		dd	5D8A0000h,1634578h	; 1e17
		dd	0A7640000h,0DE0B6B3h	; 1e18
		dd	89E80000h,8AC72304h	; 1e19

Přibližný počet číslic je již známý, ale údaj je potřeba ještě upřesnit, protože binární logaritmy mohou ležet přes hranici dekadických logaritmů. K tomu slouží tabulka Int2Mul10, která obsahuje dekadické mocniny, tedy čísla 1e10, 1e11 atd. Porovnáním původního čísla s hranicí podle vypočteného dekadického logaritmu čísla se upřesní, zda číslo leží nad hranicí nebo pod hranicí a pokud leží nad hranicí, počet číslic se zvýší o 1.


; ------------- Limit minimal number of digits (-> EDX)

UInt0ToTextBfN3:cmp	dl,bl		; check minimal number of digits
		ja	UInt0ToTextBfN4	; number of digits is OK
		mov	dl,bl		; EDX <- minimal numer of digits

Vypočtená délka čísla se zvýší podle požadovaného minimálního počtu číslic (zadaného položkou FORMPAR_Prec formátovacích parametrů).


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

UInt0ToTextBfN4:bt	ebx,FORMFLAG_Zero_b ; add zeros instead of spaces?
		jnc	UInt0ToTextBfN8	; no zeros

; ------------- Prepare width of field (-> EAX)

		movzx	eax,bh		; EAX <- minimal width of field

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

		bt	ebx,FORMFLAG_Thsn_b ; use thousand separator?
		jnc	UInt0ToTextBfN7	; not using thousand separator

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

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

; ------------- New minimal number of digits

UInt0ToTextBfN7:cmp	eax,edx		; check number of digits
		jb	UInt0ToTextBfN8	; number of digits is not greater
		xchg	eax,edx		; EDX <- new text size

Další výpočet musí zajistit doplnění nul k číslu, je-li aktivní příznakový bit FORMFLAG_Zero_b.

Do registru EAX se připraví požadovaná minimální šířka pole (z registru BH, tj. parametr FORMPAR_Width formátovacích parametrů).

Je-li požadován oddělovač řádů (přepínač FORMFLAG_Thsn_b), musí se od počtu znaků odečíst počet oddělovačů. Na 3 číslice připadá 1 znak oddělovače, přičemž před oddělovačem musí být vždy minimálně 1 číslice, proto počet číslic pro danou šířku pole EAX se vypočte inkrementací šířky pole o 1 a vynásobením číslem 3/4.

Po výpočtu počtu číslic potřebných k doplnění čísla nulami se výsledek porovná se skutečnou šířkou čísla a případně se počet číslic zvýší.


; ------------- Add thousand separator

UInt0ToTextBfN8:bt	ebx,FORMFLAG_Thsn_b ; use thousand separator?
		jnc	UInt0ToTextBfN9	; not using thousand separator
		mov	eax,edx		; EAX <- number of digits
		dec	eax		; without 1 digit
		jle	UInt0ToTextBfN9	; no digit
		imul	eax,eax,21846	; EAX <- digits*10000h/3
		shr	eax,16		; EAX <- number of separators
		add	edx,eax		; EDX <- add separators in characters

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

UInt0ToTextBfN9:xchg	eax,edx		; EAX <- text length
		pop	edi		; pop EDI
		pop	edx		; pop EDX
		ret

Je-li požadován oddělovač řádů (je nastaven přepínač FORMFLAG_Thsn_b), je potřeba k délce čísla přičíst oddělovače řádů. Na 3 číslice čísla připadá 1 oddělovač řádů, přičemž před oddělovačem musí být minimálně jedna číslice. Proto se počet oddělovačů získá snížením počtu číslic o 1 a vydělením údaje třemi. Vypočtený počet oddělovačů se přičte ke střadači délky textu v registru EDX, který je nakonec uložen do výstupního registru EAX.


Obsah / Utility / TEXTFORM / UIntToTextBufN