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

Obsah / Utility / TEXTFORM / FloatMantBCD

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

Související:

FltToTextBuf   Zformátování desetinného čísla do bufferu
FltToTextBufN   Délka textu formátovaného desetinného čísla
ExpSToTextBuf   Zformátování čísla s exponentem do bufferu, malé "e"
ExpCToTextBuf   Zformátování čísla s exponentem do bufferu, velké "E"
ExpToTextBufN   Délka textu formátovaného čísla s exponentem

FloatMantBCD - Zaokrouhlení, denormalizace a konverze mantisy na BCD

Funkce FloatMantBCD je interní funkce používaná funkcemi dekódujícími desetinné číslo na text. Zajistí zaokrouhlení mantisy čísla na požadovaný počet desetinných míst a konverzi mantisy na BCD číslice.


; -----------------------------------------------------------------------------
;          Internal - Round, denormalize and convert mantissa to BCD
; -----------------------------------------------------------------------------
; INPUT:	EAX = precision of fractional part (it will be limited to 18)
;		EBX = decimal exponent
;		ECX = pointer to extended double float number in stack (10 B)
;		ST0 = normalized float number (in range 1 incl. to 10 excl.)
;		CY = don't round (e.g. round always to 19 digits)
; OUTPUT:	EBX = new decimal exponent
;		[ECX] = mantissa digits in BCD form (19 digits, 10 bytes),
;			more significant digits are in lower address and bits
; DESTROYS:	EAX,ECX,EDX
; NOTES:	It uses 1 more register from FPU stack.
;		It pops up input number ST0.
; -----------------------------------------------------------------------------

Na vstupu funkce obsahuje registr EAX požadovanou přesnost desetinné části čísla (tj. počet desetinných míst, bude omezeno na 18). EBX obsahuje dekadický exponent. V ECX je ukazatel na číslo v zásobníku s rozšířenou přesností (tj. 10 bajtů). V registru ST0 je normalizované kladné desetinné číslo v rozsahu 1 (včetně) až 10 (vyjma). Příznak CY zakazuje zaokrouhlení čísla, číslo se zaokrouhlí vždy na 19 číslic.

Na výstupu funkce obsahuje registr EBX nový dekadické exponent. V zásobníku, kam ukazuje registr ECX, jsou navrácený číslice mantisy v BCD formátu. Je navráceno 19 číslic a to tak, že významnější číslice jsou na nižších adresách a v nižších tetrádách. Například číslo 1,23456 bude dekódováno jako 21h 43h 65h 00h 00h ...

Funkce zničí obsah registrů EAX, ECX a EDX, použije jeden další registr koprocesoru a uvolní vstupní číslo z registru ST0.


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

FloatMantBCD:	push	esi		; push ESI
		push	edi		; push EDI
		mov	edi,ecx		; EDI <- pointer to float

; ------------- Limit precision (here is CY = don't round)

		jc	short FloatMantBCD82 ; don't round
		cmp	eax,byte 18	; check precision
		ja	short FloatMantBCD8 ; limit precision
FloatMantBCD2:	mov	ch,al		; CH <- push precision

....................................

; ------------- Limit precision

FloatMantBCD8:	xor	eax,eax		; EAX <- 0
		mov	al,18		; EAX <- 18, limit precision
		jmp	short FloatMantBCD2

; ------------- Don't round

FloatMantBCD82:	cmp	eax,byte 18	; check precision
		jae	short FloatMantBCD8 ; limit precision
		mov	ch,al		; CH <- push precision
		mov	al,10*18	; EAX <- limit precision
		jmp	short FloatMantBCD3

Po úschově registrů a nastavení ukazatele cílového bufferu do EDI bude omezena požadovaná přesnost. Pokud byl nastaven příznak CY, nemá být číslo zaokrouhleno. Je-li požadovaná přesnost 18 a více, omezí se přesnost na 18 desetinných míst. Pro menší přesnost se do registru CH uloží skutečná požadovaná přesnost (jako počet požadovaných číslic) a do registru EAX omezená přesnost 18 číslic (pro další operace již vynásobeno 10x).

Má-li se číslo zaokrouhlovat, je při překročení hodnoty 18 omezen počet desetinných míst na 18. Požadovaná přesnost je uložena do registru CH.


; ------------- Round mantissa

		imul	eax,eax,10	; EAX <- precision * 10
FloatMantBCD3:	fld	tword [RoundTab+eax] ; load rounding correction
		faddp	st1,st0		; add rounding correction

....................................

; ------------- Table of rounding corrections (0.5e0 to 0.5e-18, size 180 B)

		align	8, db 0
RoundTab:
		%assign EXPN 0
		%rep	19
		dt	0.5e %+ EXPN
		%assign EXPN EXPN - 1
		%endrep

Podle hodnoty požadované přesnosti je k číslu připočtena zaokrouhlovací konstanta z tabulky RoundTab. Konstanta je volena tak, aby tvořila hodnotu 0,5 za poslední dekódovanou číslicí. Například při zaokrouhlení čísla 5,6789 na 2 desetinná místa se přičte konstanta 0,005, vznikne číslo 5,6839. Po dekódování požadovaných 1+2 číslic bude výsledné číslo 5,68. Nemá-li být číslo zaokrouhlováno, je vždy přičtena zaokrouhlovací konstanta na nejnižší možné 18. pozici, aby se zaokrouhlily chyby výpočtu.


; ------------- Check if mantissa is less than 10

		ficom	dword [Const10Num] ; compare mantissa with 10
		fstsw	ax		; AX <- FPU flags
		sahf			; get FPU flags
		jae	short FloatMantBCD9 ; mantissa is not less than 10

....................................

; ------------- Mantissa is not less than 10

FloatMantBCD9:	fld	tword [Const01Num] ; load constant 0.1
		inc	ebx		; EBX <- exponent correction
		fmulp	st1,st0		; multiply mantissa with 0.1
		jmp	short FloatMantBCD4

Po přičtení zaokrouhlovací korekce se může stát, že dojde k posunu řádu exponentu a k denormalizaci mantisy. Jedná se například o číslo 9,9999, které po zaokrouhlení na 2 desetinná místa přeteče na hodnotu 10,00. Proto je nutné mantisu opět normalizovat. Pokud mantisa dosáhla hodnotu 10, je vynásobena konstantou 0,1 a současně je inkrementací registru EBX opravena hodnota exponentu.


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

FloatMantBCD4:	fstp	tword [edi]	; store number into stack

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

		push	ebx		; push EBX (exponent)

; ------------- Load and denormalize mantissa (-> EAX:ESI:EBX, CH=precision)

		fwait			; synchronize FPU
		xor	esi,esi		; ESI <- 0
		mov	cl,[edi+8]	; CL <- exponent
		xchg	esi,[edi+4]	; ESI <- mantissa HIGH, clear buffer
		add	cl,2		; bias correction (=subtract 3fffh) + 1
		xor	eax,eax		; EAX <- 0
		xor	ebx,ebx		; EBX <- 0
		mov	[edi+8],ax	; clear buffer
		xchg	ebx,[edi]	; EBX <- mantissa LOW, clear buffer
		shld	eax,esi,cl	; EAX <- shift bits from ESI
		shld	esi,ebx,cl	; ESI <- shift bits from EBX
		shl	ebx,cl		; EBX <- shift bits

Po přípravě mantisy bude mantisa načtena do registrů. U čísla normalizovaného do rozsahu 1 až 10 bude binární exponent čísla v rozsahu 0 až 3. Mantisu je nutno denormalizovat tak, aby byl binární exponent čísla vynulován. V tom případě bude celočíselná část mantisy obsahovat první číslici čísla.

Mantisa je z registru ST0 uložena do zásobníku (na který ukazuje registr EDI). Do registru CL je načten nižší bajt binárního exponentu (vyšší bajt je 0). Přičtením 2 k CL se jednak provede korekce počátečního offsetu exponentu (binární exponent má hodnotu 3FFFh pro nulový dekadický exponent) a jednak se přednastaví počet rotací o 1 vyšší, aby se nejvyšší celočíselný bit dostal do nejvyššího registru.

Do registru ESI je načteno vyšší dvojslovo mantisy a současně je původní obsah v zásobníku vynulován. Stejně tak je načteno nižší dvojslovo mantisy a původní obsah i dalších částí v zásobníku je vynulován.

Mantisa je v registrech EAX:ESI:EBX (registr EAX je zatím nulový), rotací doleva o CL bitů pomocí instrukcí SHLD se mantisa denormalizuje tak, že registr EAX obsahuje nejvyšší číslici mantisy.


; ------------- Prepare registers

		movzx	ecx,ch		; ECX <- required number of digits - 1
		inc	ecx		; ECX <- required number of digits
		jmp	FloatMantBCD6	; store first digit

; ------------- Convert first digit

FloatMantBCD5:	mov	al,10		; EAX <- 10
		mul	ebx		; EDX:EAX <- multiple mantissa LOW
		xchg	eax,ebx		; EBX <- store new mantissa LOW
		xchg	edx,esi		; EDX <- old mant.HIGH, ESI <- new HIGH
		xor	eax,eax		; EAX <- 0
		mov	al,10		; EAX <- 10
		mul	edx		; EDX:EAX <- new mantissa HIGH
		add	esi,eax		; ESI <- add new mantissa HIGH
		xchg	eax,edx		; AL <- one digit
		adc	al,0		; AL <- carry
FloatMantBCD6:	stosb			; store first digit
		dec	ecx		; digit counter
		jz	FloatMantBCD7	; no other digit

Dále již bude pokračovat konverze mantisy na BCD číslice. Do registru ECX se připraví požadovaný počet desetinných míst (0 až 18), zvýšením o 1 se připočítá první, celočíselná, číslice. První číslice je již připravená v registru EAX, proto může být již přímo uložena do bufferu jako nižší tetráda prvního bajtu.

Mantisa v registrech ESI:EBX je binární desetinné číslo. Vynásobením deseti přeteče do nejvyššího registru EAX další číslicem která se uloží do bufferu jako nižší tetráda bajtu. Čítáním registru ECX se sleduje počet požadovaných číslic.


; ------------- Convert second digit

		mov	al,10		; EAX <- 10
		mul	ebx		; EDX:EAX <- multiple mantissa LOW
		xchg	eax,ebx		; EBX <- store new mantissa LOW
		xchg	edx,esi		; EDX <- old mant.HIGH, ESI <- new HIGH
		xor	eax,eax		; EAX <- 0
		mov	al,10		; EAX <- 10
		mul	edx		; EDX:EAX <- new mantissa HIGH
		add	esi,eax		; ESI <- add new mantissa HIGH
		xchg	eax,edx		; AL <- one digit
		adc	al,0		; AL <- carry
		shl	al,4		; shift digit 4 bits left
		or	[edi-1],al	; add digit
		loop	FloatMantBCD5	; next digit

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

FloatMantBCD7:	pop	ebx		; pop EBX (decimal exponent)
		pop	edi		; pop EDI
		pop	esi		; pop ESI
		ret

Během dekódování číslic se střídavě dekódují číslice do nižší a do vyšší tetrády bufferu. Dekódování sudých číslic je prakticky stejné jako dekódování lichých číslic s tím rozdílem, že číslice se rotuje o 4 bity k vyšším bitům a uloží se do posledního bajtu bufferu operací OR.

Ke konverzi mantisy na BCD číslice by bylo možné alternativně použít funkci koprocesoru FBSTP, v tom případě by se mantisa nejdříve převedla na celé číslo vynásobením číslem 1e+18. Převod vlastní obsluhou pomocí registrů má však několik výhod - možnost řídit počet číslic (instrukce FBSTP dekóduje vždy 18 číslic), číslice jsou v obráceném pořadí, které více vyhovuje dalšímu zpracování a rychlost konverze je buď stejná nebo při menším počtu číslic i větší oproti FBSTP.


Obsah / Utility / TEXTFORM / FloatMantBCD