| FAQ der Newsgroup de.comp.lang.assembler (d.c.l.a.) | |
|
Wie wandle ich eine Gleitkommazahl (Double) in eine Dezimalzahl (String) um? Sofern die Vorkommazahl nicht größer als ±264 (±18446744073709551616) ist, lasse man sich von folgendem Visual-C++-Programm inspirieren:
#pragma warning( disable : 4731 ) // warning C4731: frame pointer register 'ebp' modified by inline assembly code
int double2dez (double dubbel, char* dez)
{
int len;
_asm
{
fninit
sub esp, 8 ; Platz für FPU-Zwischenergebnisse
fldz ; ST(0)=0
mov word ptr [esp], 10
fild word ptr [esp] ; ST(0)=10 ST(1)=0
fstcw [esp]
or word ptr [esp], 0x0C00 ; Truncating Mode
fldcw [esp]
mov edi, dez ; Offset für alle stosb-Operationen
mov al, byte ptr [dubbel+7] ; oberstes Byte
or al, al
jns S1 ; positiv: nichts machen
mov al, '-' ;
stosb ; Negativ-Vorzeichen nach dez
S1: ; Vorkommazahl nach [esp]
fld qword ptr [dubbel] ; ST(0)=d ST(1)=10 ST(2)=0
fabs ; positiv
fld st ; ST(0)=d ST(1)=d ST(2)=10 ST(3)=0
frndint ; ST(0)=int(d) ST(1)=d ST(2)=10 ST(3)=0
fld st ; ST(0)=int(d) ST(1)=int(d) ST(2)=d ST(3)=10 ST(4)=0
fistp qword ptr [esp] ; ST(0)=int(d) ST(1)=d ST(2)=10 ST(3)=0
fsubp st(1), st ; ST(0)=d-int(d) ST(1)=10 ST(2)=0
xchg ebp, [esp+4] ; ebp:esi = Vorkommazahl, altes ebp sichern
mov esi, [esp+0]
mov ebx, 10 ; Divisor
xor ecx, ecx ; Push-Zähler
L1: ; MOD 10 auf den Stack
xor edx,edx
mov eax, ebp
div ebx
mov ebp, eax
mov eax, esi
div ebx
mov esi, eax
push dx
inc cl
or esi, esi
jne L1
or ebp, ebp
jne L1
L2: ; Stack nach dez
pop ax
or al, 0x30 ; ASCII
stosb
loop L2
mov ebp, [esp+4] ; altes ebp zurück
fcomi st(0), st(2) ; Nachkommazahl = 0 ?
jz S2 ; ja: Ausstieg
mov al, '.' ; Dezimalpunkt nach dez
stosb
mov ecx, 310 ; Maximale Anzahl an Nachkommastellen
L3: ; Nachkommazahl nach dez
fmul st, st(1) ; Eine Dezimalzahl in den Vorkommabereich holen
fist word ptr [esp] ; Vorkommazahl zwischenspeicherm
fisub word ptr [esp] ; Vorkommazahl abschneiden
mov al, byte ptr [esp]
or al, 0x30 ; Integer -> ASCII
stosb
fcomi st(0), st(2) ; noch was übrig?
jz S2 ; nein: Ausstieg
loop L3
S2: fninit ; FPU aufgeräumt zurückgeben
add esp,8 ; FPU-Platz wieder freigeben
mov byte ptr [edi], 0 ; ASCIIZ-Abschlussnull
mov eax, dez
sub edi, eax
mov dword ptr [len], edi
}
return len;
}
#pragma warning( default : 4731 ) // warning C4731: frame pointer register 'ebp' modified by inline assembly code
#include <stdio.h> // printf
int main (void)
{
char dez[350];
double dubbel = 987654321.3;
int len = double2dez (dubbel, dez);
printf ("Vorgabe: %f\n",dubbel);
printf ("String: %s\n",dez);
printf ("Len: %i\n",len);
return 0;
}
Da das Programm nicht rundet, kann man damit auch Rundungsfehler der FPU darstellen. Ralph 'rkhb' Bauer 04.08.2008 |