| FAQ der Newsgroup de.comp.lang.assembler (d.c.l.a.) | |
|
Wie wandle ich eine Dezimalzahl (String) in eine Gleitkommazahl (Double) um? Gegeben sei ein String "18.4", der während der Laufzeit des Programms von irgendwoher bezogen wurde (z.B. von einem Messgerät). Dieser soll in eine Gleitkommazahl umgewandelt werden, mit der dann weitergerechnet werden kann. Die eigentliche Umwandlung kann (und sollte) man der FPU überlassen. Allerdings kann die FPU nur Gleitkommazahlen, BCD-Zahlen und Integerzahlen einlesen. Am geschicktesten ist es, den String in einer Art und Weise in eine oder mehrere Integerzahlen umzuwandeln, die dann von der FPU eingelesen und weiterverarbeitet werden können. Der erste Schritt ist, den String in eine sogenannte "dezimale Gleitkommazahl" (decimal floating point number) umzuwandeln, also in eine Zahl ohne Komma, aber mit Exponent: Aus 18.4 soll 184e-2 (184 * 10-2) werden. Ein Beispiel in Visual-C++:
typedef struct {
int mantisse;
short exponent;
} DECIMALFLOAT;
DECIMALFLOAT dez2dfn (const char* input)
{
DECIMALFLOAT dfn;
#define DEZMINUS 1
#define EXPMINUS 2
#define EXP 4
#define POINT 8
__asm
{
xor ebx, ebx ; BH: Bitmaske, BL: Buchstabe
xor ecx, ecx ; Exponent
mov esi, input ; Zeiger auf Dezimalstring
mov dword ptr [dfn.mantisse], ebx ; dfn = {0,0}
mov word ptr [dfn.exponent], bx
mov bl, byte ptr [esi] ; 1. Buchstabe = Vorzeichen?
cmp bl, '+'
je Schleife ; Plus: nichts machen
cmp bl, '-'
jne S1 ; kein Vorzeichen
or bh, DEZMINUS ; Minus: Bitmaske setzen
Schleife:
inc esi
mov bl, byte ptr [esi]
S1: cmp bl, '0'
jb S5 ; Punkt, Stringende oder Fehler
cmp bl, '9'
ja S6 ; 'e', 'E' oder Fehler
and bl, 0x0F ; Umwandlung: Buchstabe → Zahl
test bh, EXP
jne S3
imul eax,dword ptr [dfn.mantisse],10 ; eax = Mantisse*10 + Ziffer
jo S8 ; Wertebereich überschritten
add al, bl
jnc S2
add eax, 0x0100
jo S8 ; Vorzeichenwechsel Plus → Minus
jc S8 ; Vorzeichenwechsel Minus → Plus
S2: mov dword ptr [dfn.mantisse], eax
test bh, POINT
je Schleife ; noch kein Dezimalpunkt erkannt
dec word ptr [dfn.exponent]
jmp Schleife
S3: imul cx,cx,10 ; Exponent = Exponent * 10 + Ziffer
jo S8 ; Wertebereich überschritten
add cl, bl
jnc S4
add cx, 0x0100
jo S8 ; Vorzeichenwechsel Plus->Minus
jc S8 ; Vorzeichenwchsel Minus->Plus
S4: jmp Schleife
S5: test bl, bl
jz S9
cmp bl, '.'
jne S8 ; ASCII-Wert < '0' aber kein Punkt
test bh, EXP | POINT
jne S8 ; Punkt nach Punkt oder 'e' unzulässig
or bh, POINT
jmp Schleife
S6: cmp bl, 'e'
je S7
cmp bl, 'E'
jne S8 ; ASCII-Wert > '9' aber kein 'e','E'
S7: test bh, EXP
jne S8 ; Zweimal 'e' unzulässig
or bh, EXP
inc esi
mov bl, [esi] ; nächster Buchstabe Vorzeichen?
cmp bl, '+'
je Schleife ; Plus: nichts machen
cmp bl, '-'
jne S1 ; kein Vorzeichen
or bh, EXPMINUS ; Minus: Bitmaske setzen
jmp Schleife
S8:
xor eax, eax ; Fehler: Ausstieg vor Stringende
mov dword ptr [dfn.mantisse], eax
mov word ptr [dfn.exponent], ax
jmp S12
S9:
test bh, DEZMINUS
jz S10
neg dword ptr [dfn.mantisse]
S10:
test bh, EXPMINUS
jz S11
neg cx
S11:
add word ptr [dfn.exponent], cx
S12:
}
return dfn;
}
Nun kann man die FPU mit den Zahlen füttern und umwandeln lassen:
double dfn2double (DECIMALFLOAT dfn)
{
double dubbel;
__asm
{
push 10 ; Faktor 10 in die FPU
fild dword ptr [esp]
add esp, 4
fild dword ptr [dfn.mantisse]
movsx ecx, word ptr [dfn.exponent]
test ecx, ecx
jz S3 ; Sprung, wenn ecx=0
jns S1 ; Sprung, wenn ecx positiv
neg ecx
S1: fdiv st, st(1) ; Exponent < 0: Komma nach links versetzen
loop S1
jmp S3
S2: fmul st, st(1) ; Exponent > 0: Komma nach rechts versetzen
loop S2
S3: fstp qword ptr [dubbel]
fninit ; FPU aufräumen
}
return dubbel;
}
Zum Testen noch schnell eine Main-Funktion gebastelt:
#include <stdlib.h> // strtod
#include <stdio.h> // printf
void main (void)
{
DECIMALFLOAT dfn;
char * Eingabe, * stopstring;
Eingabe = "-18.4e-4";
double dubbel = 0;
double testdubbel = strtod (Eingabe,&stopstring);
printf ("Eingabe: %s\n", Eingabe);
dfn = dez2dfn (Eingabe);
printf ("Mantisse: %i Exponent: %i\n",dfn.mantisse,dfn.exponent);
dubbel = dfn2double (dfn);
printf ("dubbel: %e\n",dubbel);
printf ("testdubbel: %e\n",testdubbel);
}
Ralph 'rkhb' Bauer Aug 2008 |