Wie wandle ich eine Binärzahl (String) in eine Gleitkommazahl (Double) um?
Die nachfolgende Funktion wandelt eine Binärzahl, die in Exponentialschreibweise
als ASCIIZ-String vorliegt (bzw. ein String, der mit irgendeinem unzulässigen Buchstaben endet),
in ein Double im Registerpaar EDX:EBX um.
Die Funktion kann keine denormalisierte Gleitkommazahl herstellen.
Die Binärzahl kann in folgenden Formaten vorliegen:
1010 Folge von Binärziffern
-1010 negative Binärzahl
1010b Folge von Binärziffern mit "unzulässigem" Zeichen als Abschluss
1010 1010 Folge von Binärziffern mit Leerzeichen
1010.1010 Vorkommazahl Dezimalpunkt Nachkommazahl
1010e1010 Exponentialschreibweise
1010e-1010 Exponentialschreibweise
Nasm
BITS 32
SEGMENT .text
%define NEGATIV 1
%define EXPMINUS 2
%define EXP 4
%define POINT 8
%define LEADZ 16
%define OVERFLOW 32
%define NULL 64
%define INFINITE 128
Bin2Double: ; Übergabe ESI: Zeiger auf ASCIIZ
xor eax, eax ; AH: Bitmaske; AL: jeweilige Binärziffer
xor ecx, ecx ; Exponent in der Exponentialschreibweise
mov edi, 1023 ; Bias
xor edx, edx ; EDX:EBX Ergebnis
xor ebx, ebx
.Vorzeichen:
lodsb
cmp al, '+'
je .M_Parser ; Plus: nichts machen
cmp al, '-'
jne .SkipLods ; kein Vorzeichen
or ah, NEGATIV ; Minus: Bitmaske setzen
.M_Parser:
lodsb
.SkipLods:
cmp al, '0'
jb .Sonderzeichen
jne .Eins
test ah, LEADZ ; führende Null?
jnz .Verarbeitung ; nein
test ah, POINT ; führende Null im Nachkommabereich?
jz .M_Parser ; nein: continue
sub edi, 1 ; ja: Exponent dekrementieren
jnc .M_Parser
or ah, NULL ; Unterlauf (Exponent zu klein)
jmp .Umwandlung
.Eins:
cmp al, '1'
ja .Exponent ; keine Binärziffer: Exponentialschreibweise probieren
.Verarbeitung:
or ah, LEADZ
bt edx, 20 ; 52. Bit gesetzt: Überlauf (zu viele Bits)
jc .Overflow
test ah, POINT
jnz .Mantisse
inc edi ; Exponent inkrementieren, wenn Vorkommazahl
test edi, 100000000000b ; Überlauf (Exponent zu groß)
jz .Mantisse
or ah, INFINITE
jmp .Umwandlung
.Mantisse:
shr al, 1
rcl ebx, 1
rcl edx, 1
jmp .M_Parser ; nächste Binärziffer
.Overflow:
test ah, OVERFLOW ; erster Überlauf?
jnz .SkipRound ; nein: keine Rundung
or ah, OVERFLOW
shr al, 1
adc ebx, 0 ; Rundung
adc edx, 0
.SkipRound:
test ah, POINT ; Überlauf im Nachkommabereich?
jnz .Umwandlung ; ja: Ende
inc edi ; nein: Exponent erhöhen, sonst nichts
test edi, 100000000000b ; Überlauf (Exponent zu groß)
jz .M_Parser
or ah, INFINITE
jmp .Umwandlung
.Sonderzeichen:
cmp al, 32 ; Leerzeichen?
je .M_Parser ; ja: nächste Binärziffer
cmp al, 46 ; Dezimalpunkt
jne .Umwandlung ; nein: Ausstieg
test ah, POINT
jnz .Umwandlung ; Dezimalpunkt bereits gefunden
or ah, POINT
jmp .M_Parser
.Exponent:
cmp al, 'e'
je .E1
cmp al, 'E'
jne .Umwandlung
.E1:
test ah, EXP
jnz .Umwandlung ; Expontialzeichen bereits gefunden
or ah, EXP
lodsb
cmp al, '+'
je .E_Parser ; Plus: nichts machen
cmp al, '-'
jne .E_SkipLods ; kein Vorzeichen
or ah, EXPMINUS ; Minus: Bitmaske setzen
.E_Parser:
lodsb
.E_SkipLods:
cmp al, 32
je .E_Parser
cmp al, '0'
jb .Umwandlung
cmp al, '1'
ja .Umwandlung
shr al, 1
rcl ecx, 1
jnc .E_Parser
.E_Overflow:
test ah, EXPMINUS
jnz .E2
or ah, INFINITE
jmp .Umwandlung
.E2:
or ah, NULL
jmp .Umwandlung
.Umwandlung: ; EBX:EDX: Mantisse; EDI: normalisierter Exponent; ECX: angegebener Exponent
dec edi
test ah, EXP
jz .SkipExp
test ah, EXPMINUS
jz .ExpPlus
sub edi, ecx
jnc .SkipExp
or ah, NULL
jmp .SkipExp
.ExpPlus:
add edi, ecx
test edi, 100000000000b ; Überlauf (Exponent zu groß)
jz .SkipExp
or ah, INFINITE
.SkipExp:
test ah, NEGATIV
jz .SkipMinus
or edi, 100000000000b ; Negative Zahl: Vorzeichen-Bit setzen
.SkipMinus:
test ah, NULL
jz .SkipNil
and edi, 100000000000b ; Exponent=0, Mantisse=0, Vorzeichen bleibt
xor edx, edx
xor ebx, ebx
jmp .Ende
.SkipNil:
test ah, INFINITE
jz .SkipInfinite
or edi, 11111111111b ; Exponent=(-1), Mantisse=0
xor edx, edx
xor ebx, ebx
jmp .Ende
.SkipInfinite:
mov ecx, 31
bsr eax, edx
jnz .SkipNull ; gesetztes Bit in EDX gefunden
xchg edx, ebx ; EDX=EBX, EBX=0
bsr eax, edx
jnz .SkipNull ; gesetztes Bit in EBX gefunden
and edi, 100000000000b ; Sonderfall Null (Exponent=0, Mantisse=0, Vorzeichen bleibt)
xor edx, edx
xor ebx, ebx
jmp .Ende
.SkipNull:
sub ecx, eax ; alles nach ganz links shiften
shld edx, ebx, cl
shl ebx, cl
shld edx, ebx, 1 ; führende Eins verschwindet
shl ebx, 1
.Ende:
shrd ebx, edx, 12 ; Vorzeichen und BIAS von rechts hineinshiften
shrd edx, edi, 12
ret ; Ergebnis EDX:EBX: Double
Ralph 'rkhb' Bauer Aug 2008