Funktion "FloatToString" und "StringToFloat" selber schreiben
-
hat jemand von euch diese funktionen schon mal selber geschrieben? für ganzzahlen ist das ja nicht schwer, aber für floats dürfte sich das schon etwas schwieriger gestalten. besonders wichtig ist das man ALLE nur denknbaren fälle behandel, wie z.b.:
00000000000000000000000000000000000001.00000000000000000000000000000000000000000000000000000000000000000000005f
das als string umgewandelt in eine float-variable müsste dann 1.0 ergeben (da abgerundet wird). hat ajemabnd von euch ein par gute vorschläge wie man das für floats realisieren kann?
für ganzzahlen habe folgende lösung die alle fälle behandelt also auch sowas:
0000000000000000000000000000000000000000000001234
oder
1000000000000000000000000000000000000000000001234
für jeden den es interessiert:
bool CString::IsInteger(void) { unsigned short I; if (m_Length==0) { return(false); } else { for (I=0;I<m_Length;I++) if ((m_Buffer[I]=='+') || (m_Buffer[I]=='-')) if ((I!=0) || (m_Length<=1) || ((m_Buffer[1]<48) || (m_Buffer[1]>57))) break; else if ((m_Buffer[I]<48) || (m_Buffer[I]>57)) break; if (I==m_Length) return(true); else return(false); } } void CString::TransformToInteger(signed char *SignedChar) { unsigned short I; unsigned short Length; signed char Value; signed char Base; signed char Count; bool ZeroTest; if (m_Length!=0) if (IsInteger()==true) if (m_Buffer[0]=='-') { Value=0; Base=1; Length=m_Length-1; for (I=0;I<Length;I++) { Count=m_Buffer[m_Length-1-I]-48; if (I<2) { Value=Value-(Count*Base); Base=Base*10; } else { if ((Count>=2) || ((Count==1) && (Value<=-28))) { Value=-127-1; } else { ZeroTest=false; for (I++;I<Length;I++) if ((m_Buffer[m_Length-1-I]-48)!=0) { ZeroTest=true; break; } if (ZeroTest==true) Value=-127-1; else Value=Value-(Count*Base); } break; } } (*SignedChar)=Value; } else { Value=0; Base=1; if (m_Buffer[0]=='+') Length=m_Length-1; else Length=m_Length; for (I=0;I<Length;I++) { Count=m_Buffer[m_Length-1-I]-48; if (I<2) { Value=Value+(Count*Base); Base=Base*10; } else { if ((Count>=2) || ((Count==1) && (Value>=27))) { Value=127; } else { ZeroTest=false; for (I++;I<Length;I++) if ((m_Buffer[m_Length-1-I]-48)!=0) { ZeroTest=true; break; } if (ZeroTest==true) Value=127; else Value=Value+(Count*Base); } break; } } (*SignedChar)=Value; } } void CString::TransformToInteger(unsigned char *UnsignedChar) { unsigned short I; unsigned short Length; unsigned char Value; unsigned char Base; unsigned char Count; bool ZeroTest; if (m_Length!=0) if (IsInteger()==true) if (m_Buffer[0]=='-') { (*UnsignedChar)=0; } else { Value=0; Base=1; if (m_Buffer[0]=='+') Length=m_Length-1; else Length=m_Length; for (I=0;I<Length;I++) { Count=m_Buffer[m_Length-1-I]-48; if (I<2) { Value=Value+(Count*Base); Base=Base*10; } else { if ((Count>=3) || ((Count==2) && (Value>=55))) { Value=255; } else { ZeroTest=false; for (I++;I<Length;I++) if ((m_Buffer[m_Length-1-I]-48)!=0) { ZeroTest=true; break; } if (ZeroTest==true) Value=255; else Value=Value+(Count*Base); } break; } } (*UnsignedChar)=Value; } } void CString::TransformToInteger(signed short *SignedShort) { unsigned short I; unsigned short Length; signed short Value; signed short Base; signed short Count; bool ZeroTest; if (m_Length!=0) if (IsInteger()==true) if (m_Buffer[0]=='-') { Value=0; Base=1; Length=m_Length-1; for (I=0;I<Length;I++) { Count=m_Buffer[m_Length-1-I]-48; if (I<4) { Value=Value-(Count*Base); Base=Base*10; } else { if ((Count>=4) || ((Count==3) && (Value<=-2768))) { Value=-32767-1; } else { ZeroTest=false; for (I++;I<Length;I++) if ((m_Buffer[m_Length-1-I]-48)!=0) { ZeroTest=true; break; } if (ZeroTest==true) Value=-32767-1; else Value=Value-(Count*Base); } break; } } (*SignedShort)=Value; } else { Value=0; Base=1; if (m_Buffer[0]=='+') Length=m_Length-1; else Length=m_Length; for (I=0;I<Length;I++) { Count=m_Buffer[m_Length-1-I]-48; if (I<4) { Value=Value+(Count*Base); Base=Base*10; } else { if ((Count>=4) || ((Count==3) && (Value>=2767))) { Value=32767; } else { ZeroTest=false; for (I++;I<Length;I++) if ((m_Buffer[m_Length-1-I]-48)!=0) { ZeroTest=true; break; } if (ZeroTest==true) Value=32767; else Value=Value+(Count*Base); } break; } } (*SignedShort)=Value; } } void CString::TransformToInteger(unsigned short *UnsignedShort) { unsigned short I; unsigned short Length; unsigned short Value; unsigned short Base; unsigned short Count; bool ZeroTest; if (m_Length!=0) if (IsInteger()==true) if (m_Buffer[0]=='-') { (*UnsignedShort)=0; } else { Value=0; Base=1; if (m_Buffer[0]=='+') Length=m_Length-1; else Length=m_Length; for (I=0;I<Length;I++) { Count=m_Buffer[m_Length-1-I]-48; if (I<4) { Value=Value+(Count*Base); Base=Base*10; } else { if ((Count>=7) || ((Count==6) && (Value>=5535))) { Value=65535; } else { ZeroTest=false; for (I++;I<Length;I++) if ((m_Buffer[m_Length-1-I]-48)!=0) { ZeroTest=true; break; } if (ZeroTest==true) Value=65535; else Value=Value+(Count*Base); } break; } } (*UnsignedShort)=Value; } } void CString::TransformToInteger(signed long *SignedLong) { unsigned short I; unsigned short Length; signed long Value; signed long Base; signed long Count; bool ZeroTest; if (m_Length!=0) if (IsInteger()==true) if (m_Buffer[0]=='-') { Value=0; Base=1; Length=m_Length-1; for (I=0;I<Length;I++) { Count=m_Buffer[m_Length-1-I]-48; if (I<9) { Value=Value-(Count*Base); Base=Base*10; } else { if ((Count>=3) || ((Count==2) && (Value<=-147483648))) { Value=-2147483647-1; } else { ZeroTest=false; for (I++;I<Length;I++) if ((m_Buffer[m_Length-1-I]-48)!=0) { ZeroTest=true; break; } if (ZeroTest==true) Value=-2147483647-1; else Value=Value-(Count*Base); } break; } } (*SignedLong)=Value; } else { Value=0; Base=1; if (m_Buffer[0]=='+') Length=m_Length-1; else Length=m_Length; for (I=0;I<Length;I++) { Count=m_Buffer[m_Length-1-I]-48; if (I<9) { Value=Value+(Count*Base); Base=Base*10; } else { if ((Count>=3) || ((Count==2) && (Value>=147483647))) { Value=2147483647; } else { ZeroTest=false; for (I++;I<Length;I++) if ((m_Buffer[m_Length-1-I]-48)!=0) { ZeroTest=true; break; } if (ZeroTest==true) Value=2147483647; else Value=Value+(Count*Base); } break; } } (*SignedLong)=Value; } } void CString::TransformToInteger(unsigned long *UnsignedLong) { unsigned short I; unsigned short Length; unsigned long Value; unsigned long Base; unsigned long Count; bool ZeroTest; if (m_Length!=0) if (IsInteger()==true) if (m_Buffer[0]=='-') { (*UnsignedLong)=0; } else { Value=0; Base=1; if (m_Buffer[0]=='+') Length=m_Length-1; else Length=m_Length; for (I=0;I<Length;I++) { Count=m_Buffer[m_Length-1-I]-48; if (I<9) { Value=Value+(Count*Base); Base=Base*10; } else { if ((Count>=5) || ((Count==4) && (Value>=294967295))) { Value=4294967295; } else { ZeroTest=false; for (I++;I<Length;I++) if ((m_Buffer[m_Length-1-I]-48)!=0) { ZeroTest=true; break; } if (ZeroTest==true) Value=4294967295; else Value=Value+(Count*Base); } break; } } (*UnsignedLong)=Value; } }
[ Dieser Beitrag wurde am 26.06.2003 um 10:33 Uhr von KXII editiert. ]
-
hm, nun ja.
Kann es sein, dass du die TransformToInteger mehrmals eingefügt hast?
Hier mal meine Idee dazu:
Also, wenn du einen String hast, zähle die Nachkommastellen.
Wenn es mehr sind, als float speichern kann, dann runde genau auf die Stellen, die float speichern kann.
Dann zähle die Nachkommastellen und lösche dann das Komma as dem String raus.
Wandle das in long long um (mit Transform to Integer) und teile es durch 10^NachkommastellenDann müsste eigentlich float rauskommen.
Gruß, MAxi
-
ich habe sie mehrmals eingefügt, weil es auch mehrere integer gibt (signed char, unsigned char, signed short, unsigned short, signed long und unsigned long; nur der vollständigkeithalber)
so ähnlich habe ich mir das auch schon gedacht, aber ich sehe ein problemchen. man kann nicht die zahlen mit denen man hantiert als 10er potenzen behandeln, da ja floats intern eine 2er potenz darstellung haben, und da können sich durchaus rechenfehler einschleichen. ich glaube man muss im algorithmus die floats auch in mantisse, exponent und vorzeichen zerlegen und dann in getrennter form die umwandlung vornehmen. ausserdem gibt es ja auch noch sowas wie NaN's etc. ich weis nicht genau was es da noch für sonderfälle gibt. es gibt auf jedenfall "normale" und "unormale" floats, es gibt NaN's und unendlichkeiten. im dem buch "das Assmbler Buch" von Trutz Eyke Podschun 4. Auflage wird auf den Seiten 852-856 (falls das buch jemand hat) die Problematik ganz gut darstellt. das umwandeln ist nicht so trivial wie es zunächst aussieht. falls doch dann verstehe ich nicht so ganz wie.
[ Dieser Beitrag wurde am 26.06.2003 um 11:00 Uhr von KXII editiert. ]
-
ich glaube man könnte beim umwandeln von strings in floats so vorgehen:
1.) zuerst einmal testen ob es eine float ist. das habe ich so realisiert (kann man aber auch anders machen):
bool CString::IsFloat(void) { unsigned short I; bool Point; if (m_Length==0) { return(false); } else { Point=false; for (I=0;I<m_Length;I++) if ((m_Buffer[I]=='+') || (m_Buffer[I]=='-')) if ((I!=0) || (m_Length<=1) || ((m_Buffer[1]<48) || (m_Buffer[1]>57))) break; else if (m_Buffer[I]=='.') { if ((Point==true) || (I<1) || (I>=(m_Length-1)) || ((m_Buffer[I-1]<48) || (m_Buffer[I-1]>57)) || ((m_Buffer[I+1]<48) || (m_Buffer[I+1]>57))) break; Point=true; //Es darf nur einen einzigen Dezimalpunkt geben } else { if ((m_Buffer[I]<48) || (m_Buffer[I]>57)) break; } if (I==m_Length) return(true); else return(false); } }
eine string ist genau dann eine float-variable, wenn er maximal einen dezimalpunkt hat und, wenn er einen hat, vor und hinter dem punkt mindestens eine zahl steht. erne float darf auch eine plus oder minus zeichen haben aber nur eins und nur am anfang des strings. und der string darf nur aus den zeichen "0123456789+-." bestehen.
2.) string umwandeln (der schwierigere teil)
exponent mantisse und vorzeichen getrennt ermitteln.
3.)
alle sonderfälle behandeln. also unendlichkeiten und sowas.
4.)
falls die zahl kein sonderfall war die umwandlung vornehemen
punkt 2 und 4 sind für mich die interessantesten fälle, und ich habe keinen wirklichen ansatz dafür.
das umwandeln von FloatToString dürfte einfacher sein. aber auch hier weis ich nicht so ganz genau wie man an die einzelnen ziffern rankommen soll. man darf ja nicht, wie bei ganzzahlen, mit einer division und einer restbildung die nachkommastellen ermitteln, da das zu rechenfehlern führt. bei den vorkommastellen dürfte die div/mod variante funktionieren.
-
Hi,
nachden der String durchgetestet ist, einen Punkt enthält, und wahlweise ein Vorzeichen. Dann so zum umwandeln:
double StringToDouble(string zahl){ double erg=0; int iter = 0; int multi=0; int vorzeichen=1; // Test nach dezimalpunkt Wenn keiner Da, dann wird einer angehängt if( zahl.find('.') == -1 ) zahl.append(1,'.'); // Vorzeichen if (zahl[0]=='-') { vorzeichen=-1; zahl.erase(0,1); } if (zahl[0]=='+') zahl.erase(0,1); // Ganzzahliger Teil iter = zahl.find('.')-1; while ( iter>=0 ) { erg += ( zahl[iter] -'0' ) * pow(10,multi); ++multi; --iter; } //gebrochener Teil multi=1; iter=zahl.find('.')+1; while( iter< zahl.length() ) { erg += ( zahl[iter]-'0' ) * (1/pow(10,multi)) ; ++multi; ++iter; } return erg*vorzeichen; }
Ups, habs jetzt als Funktion statt als Methode. Sollte ja aber kein Prob darstellen.
grüße Con@n
EDIT: Ich hab noch eine Abfrage nach dem Dezimalpunkt eingebaut. Jetzt können auch ganze zahlen umgewandelt werden.
[ Dieser Beitrag wurde am 26.06.2003 um 12:46 Uhr von Con@n editiert. ]
-
@con@n
beim gebrochnen teil rechnest du dir aber mit:
erg += ( zahl[iter]-'0' ) * (1/pow(10,multi)) ;
rundungsfehler ein.
man kann nur vielfache von 2er potenzen mit floats darstellen (wenn mich nicht alles täuscht). z.b. ist 9*(1/10)=0.89999998 und nicht 0.9.
oder sehe ich das falsch?
-
Jep leider ja.
bei .9 kommt mit meiner funtkion .900000000000000002 raus. Man kann das Ergebnis ja noch runden in 'multi' steht ja am Ende die Zahl der Nachkommastellen.
Zahlen, die sich mit dem begrenzten Vorrat von 24 und 53 bit bei float und double nicht darstellen lassen sind eh immer Fehlerhaft. Daher wird es am Ende auch nicht viel ausmachen.
EDIT: probier mal
double x=.9; std::cout << std::setprecision(20) << x;
grüße Con@n
[ Dieser Beitrag wurde am 26.06.2003 um 12:57 Uhr von Con@n editiert. ]
-
stimmt, viel ausmachen tut das nicht. aber mir geht es darum eine vollkommene funktion zu schreiben. naja was solls. momentan habe ich einfach keine zeit das problem genauer zu untersuchen. ich würde bestimmt irgendwann den lösungsweg finden. nur wo liegt da der nutzen? eine wochen arbeit für eine simple funktion, die man normalerweise als gegeben vorausetzt...
vielleicht hat ja irgendjemand eine perfekte lösung und postet sie hier.
danke schon mal im voraus!
-
Hi,
das Problem ist ja nicht die Funktion zum umrechnen, sondern der Datentyp der das Ergebnis aufnehmen soll. Solange dieses Problem nicht gelöst ist, kann man auch keine genaue Umwandlung schreiben.
grüße Con@n
-
ehmm....
ganzahlen, die die einzelteile (mantisse, exponent, vorzeichen) aufnehmen?
man kann doch hingehen und den string in vor und nachkommateil zerlegen und dann beide getrennt als ganzzahl in die entsprechende schreibweise umwandeln, und dann, wenn man alle teile hat, rechnet man diese so um bis die darstellung folgende ist:
s * 1,x * 2^n
x=mantisse und n=exponent und s=vorzeichen
dann kann man ja die float variable "auf einmal" berechnen und hat dann nur noch die "natürlichen" rundungsfehler. ob das jetzt auch wirklich alles 100% funktioniert weis ich nicht, aber ich glaube schon, das man "verlustfrei" einen string in float und umgekehrt umwandeln kann.
-
Hi,
mal schauen, ob ich Dich verstanden hab (komm grad aus dem Biergarten). Also, ein float ist aufgeteilt in 1 bit Vorzeichen, 7 bit Exponent und 24 bit Mantisse. In der Mantisse steht aber nicht direkt ein ganzzahliger Wert, der nur um Kommastellen verückt wird. Die Nachkommastellen berechnen sich nach "1/pow(2,n)", wobei n für die stelle nach dem Komma steht.Also 0b0.1 ist dezimal 0.5, 0b0.01 ist 0.25, 0b.001 ist 0.125
deswegen kann man nicht sagen, wenn in der Mantisse z.Bsp 0d43125 (macht 0b1010100001110101) steht und um 3 Stellen geschoben wird, daß dann 43.125 rauskommt.
Für 43.125 steht in der Mantisse 0b101011001 und es wird um drei Stellen verschoben.
daher glaub ihc, daß meine Funktion doch recht genau ist, und der Fehler fast ausschließlich im Datentyp begründet ist.grüße Con@n