Hallo Leute ich steh vor dem Problem eine Zahl in eine Gleitkomme zahl umzuwandeln. Also der Zahlenbereich geht von -671 088,64 bis 670 760,96. Die Auflösung ist aber auf ganze Zahlen beschränkt. Ich habe also eine Array zur Verfügung indem die umzurechnende Zahl steht. Also z.Bsp: -27 da steht dann im Array Ar1[0]='-'; Ar1[1]='0'; Ar1[2]='0'; Ar1[3]='0'; Ar1[4]='0'; Ar1[5]='2'; Ar1[6]='7'; bis dahin nicht neues. Aber ich muss die Zahl jetzt in folgendes Format umwandeln: Float Value MEEE EMMM MMMM MMMM Encoding: FloatValue = (0,01*M)*2(E) E = [0 … 15] M = [-2 048 … 2 047], two’s complement notation For all Datapoint Types 9.xxx, the encoded value 7FFFh shall always be used to denote invalid data Wie kann ich die Zahl -27 in das Format MEEE EMMM MMMM MMMM am effizientesten umwandeln?
Also du bekommst die Zahl in einem ASCII-Array geliefert und willst die nach float konvertieren? Oder hab ich das falsch verstanden?
ja genau aber eben in dem Format Format MEEE EMMM MMMM MMMM
Und das soll da wieder als ASCII oder in interner (Binär)-Codierung stehen?
in Binär Codierung eben in dem Format MEEE EMMM MMMM MMMM M - Vorzeichen EEEE - Exponent mmmmmmmmmmm - Mantisse
Was ist da jedes einzelne E bzw. M? Ein Bit oder ein CHaracter. Zunächst mal würde ich mir aus der ASCII Darstellung der Zahl, die du hast einen richtige Zahl machen, damit da auch gerechnet werden kann. atof (atoi?) oder dtostrf ist hier dein Freund. Liegt die Zahl erst mal binär im Speicher, dann wird erst mal Mantisse und Exponent bestimmt und davon dann die Bitdarstellung zusammengebaut. Du hast 11 Mantissenbits. d.h. die Mantisse kann nicht größer als 2048 sein. FloatValue = (0,01*M)*2(E) Du hast den Float Value und suchst daher die beiden Zahlen M und E, so dass obige Gleichung erfüllt ist. Denk dir was aus! Es wird auch sinnvoll sein, M möglichst groß zu wählen. Auf jeden Fall aber so, dass E möglichst ganzzahlig wird. Und dann werden E und M in Bits zurechtgeschoben und das Endergebnis zusammengebaut.
Ich glaube nicht, daß atof etc. hier angesagt sind. Eher vermute ich, daß das eine reine Übungsaufgabe ist und er das per pedes programmieren soll. Sonst würde doch niemand eine Gleitkommadarstellung in 2 Byte machen... (Weiterhin vermute ich, daß er das selbst machen sollte, oder bekommen wir dann den Schein, wenn es funktioniert?)
Wenn ich das richtig verstehe, will er sich ein 16-Bit float basteln. Das muß man wohl zu Fuß machen, ich kenne jedenfalls keinen Compiler, der das kann. Üblich sind 32-, 64- oder 80-Bit floats. Manche PIC-Compiler sollen wohl auch ein 24-Bit float haben, aber das ist schon hart an der Grenze der Sinnhaftigkeit. Peter
Es könnte schon eine reale Aufgabe sein. Immerhin gibt es die Beschränkung auf: nur ganzzahlige Ausgangswerte. > Wie kann ich die Zahl -27 in das Format MEEE EMMM MMMM MMMM am > effizientesten umwandeln? Daher jetzt von mir die süffisante Gegenfrage: Welche nicht effiziente Lösung hast du denn bisher? Hint: Wenn wir das Vorzeichen einmal weglassen, so kommt mir auf Anhieb ein C 3 Zeiler in den Sinn, sobald die ASCII Zahl erst mal mit atoi in einen 4-Byte int umgewandelt worden ist. Hint2: Er hat damit zu tun, dass bei Erhöhung des Exponenten um 1, die Mantisse halbiert wird um die Zahl nicht zu verändern (Im Rahmen der Genauigkeit). Dazu noch die Forderung, dass die Mantisse kleiner als 2048 sein muss und schon ergibt das eine schöne kleine Schleife die im wesentlichen aus einem Vergleich, einem Rechtsschieben und einem Inkrement besteht. Dann in der 3. Zeile die Einzelteile schön zurechtschieben und zusammenodern. Mit Vorzeichen wirds noch ein klein wenig komplizierter aber zur Not kann man ja auch 2 Fallunterscheidungen für positiv und negativ machen.
Hallo Leute ich wollte mich mal wieder zu Wort melden, da ich diese Umwandlung jetzt benötige. Danke für eure Hinweise. Also erstens: Das Vorzeichen soll erstmal außer acht bleiben das wollt ich am ende lösen: Das Vorzeichenbit auf 1 und Mantissenbits umdrehen. Ich habe bereits einen Ansatz
1 | exponent=0x3800; // mit Exponent 7 wird angefangen |
2 | eis5=lux>>1; |
3 | eis5+=lux>>2; |
4 | eis5+=lux>>5; |
5 | while (eis5lux > 0x07FF) |
6 | { // Exponent erhöhen falls Mantisse zu groß |
7 | eis5lux=eis5lux>>1; |
8 | exponent+=0x0800; |
9 | }
|
10 | eis5+=exponent; |
Leider passt der Code noch nicht richtig. Bei der Zahl 450000 kommt nach der obigen Rechnung 7D5D raus, aber richtig sollte sein 46DD. Aber das ich nach rechts shiften muss hab ich schonmal kapiert :-) Könntet ihr mir noch ein paar Tips geben? Das ist übrigens nicht für irgend nen Schein sondern für mich Zuhaus. Es sollen Temperaturen über den Knx Bus gehen und die sind nunmal so kodiert.
Christian Hohmann schrieb:
> Könntet ihr mir noch ein paar Tips geben?
Es gibt nicht DAS GLEITKOMMAFORMAT.
Sondern jeder kocht da sein eigenes Süppchen.
Solange du also keine Beschriebung lieferst, wie das Zielformat aussehen
muss, kann dir keiner helfen.
exponent=0x3800; // mit Exponent 7 wird angefangen
Ich würde das erst mal alles in einem unsigned char ganz normal
abhandeln. D.h. Exponent beginnt bei 0 und wird um 1 hochgezählt /
verringert. Um den ganzen Sonderkram, wie Versatz um 7, Shifting an die
richtige Position etc. würde ich mich erst nachher kümmern, wenn die
tatsächlich zu übertragenden Bytes zusammengesetzt werden.
Dann kann man sich auch die Einzelteile zb über UART ansehen und besser
nachvollziehen, wie das Endergebnis in der Schleife entsteht.
Manchmal ist es nicht besonders schlau, gleich zu clever zu sein und
zuviel auf einmal machen zu wollen.
eis5lux=eis5lux>>1;
Schreib /2 wenn du /2 meinst.
Was berechnet diese Sequenz?
eis5=lux>>1;
eis5+=lux>>2;
eis5+=lux>>5;
kannst du das auch in normaler Arithmetik ausdrücken? Wie gesagt:
Manchmal ist es nicht schlau, zu clever zu sein.
Christian Hohmann schrieb: > Leider passt der Code noch nicht richtig. Bei der Zahl 450000 kommt nach > der obigen Rechnung 7D5D raus, aber richtig sollte sein 46DD. Heißt das, das Programm liefert 7D5D? Wieso sollte 46DD richtig sein? Ich erhalte von Hand für 450000 den Wert 7D5D. 46DD entspräche dann 4497,92.
Danke das ihr euch noch mit mir beschäftigt. > Solange du also keine Beschriebung lieferst, wie das Zielformat aussehen > muss, kann dir keiner helfen. Steht ganz oben: Zielformat 2byte aufgeteielt in : VEEE EMMM MMMM MMMM : V=Vorzeichen; E=Exponent; M= Mantisse Also hier nochmal worum es geht mit einem Beispiel: To encode the value -30°C with a precision of 1/100: - first rewrite the value - 30°C = -3000.1/100°C (precision determined implicitly by the value) - the 11 bit mantissa allows a maximum value of 2047 to be encoded; in this case, we will have to introduce an exponent of 2^1, since 3000 > 2047: 3000 = 1500 * 2^1 mantissa exponent 1500 is encoded as 0 101 1101 1100 sign mantissa - changing the sign as of 1500 to -1500 gives: 1 010 00100100 sign mantissa - as the final result we get: S E E E E M M M M M M M M M M M Value 1 0 0 0 1 0 1 0 0 0 1 0 0 1 0 0 -3000 So soll das aussehen. Daraus schließe ich wenn ich z.Bsp. Eine Temperatur von 110° habe, muss ich erstmal 2 Nullen dran hängen, also 11000 und diese Zahle dann in das geforderte Format umwandeln. Joa dann hat mir jemand das so mit dem Shiften gesagt das ich das so machen soll. also das ein shift um 2 nach rechts die zahl halbiert weis ich, warum da aber auch nach 5 rechts geshiftet wird konnt ich mir auch nicht erklären. Deswegen hab ichs ja geschrieben um euch zu fragen. ich hoffe jetzt wird klarer was ich vor hab. Ist das schonmal richtig das geprüft wird ob die Zahl größer als 0x07FF ist? denn dann müsste der exponent erhöht werden und die zahl kleiner sein. Hofe soweit hab ich schonmal richtig gedacht.
Achso eh ichs vergesse wie ich auf die hexwerte komme die eigentlich rauskommen sollten: http://www.tapko.de/index.php/Tools/31/0/ bei dpt9 kann man seinen Wert eintragen und sieht dann rechts den hexwert
Christian Hohmann schrieb: > Danke das ihr euch noch mit mir beschäftigt. >> Solange du also keine Beschriebung lieferst, wie das Zielformat aussehen >> muss, kann dir keiner helfen. > > Steht ganz oben: > Zielformat > 2byte aufgeteielt in : > VEEE EMMM MMMM MMMM : V=Vorzeichen; E=Exponent; M= Mantisse Mein Fehler. > Also hier nochmal worum es geht mit einem Beispiel: > > To encode the value -30°C with a precision of 1/100: > - first rewrite the value > - 30°C = -3000.1/100°C (precision determined implicitly by the value) > - the 11 bit mantissa allows a maximum value of 2047 to be encoded; > in this case, we will > have to introduce an exponent of 2^1, since 3000 > 2047: > 3000 = 1500 * 2^1 > mantissa exponent > > 1500 is encoded as > 0 101 1101 1100 > sign mantissa > - changing the sign as of 1500 to -1500 gives: > 1 010 00100100 > sign mantissa > - as the final result we get: > S E E E E M M M M M M M M M M M Value > 1 0 0 0 1 0 1 0 0 0 1 0 0 1 0 0 -3000 > > So soll das aussehen. > Daraus schließe ich wenn ich z.Bsp. Eine Temperatur von 110° habe, muss > ich erstmal 2 Nullen dran hängen, Wegen dem hier To encode the value -30°C with a precision of 1/100 > also 11000 und diese Zahle dann in das > geforderte Format umwandeln. > > Joa dann hat mir jemand das so mit dem Shiften gesagt das ich das so > machen soll. Grundsätzlich. Schreib mathematische Sachen so hin, wie DU das gut lesen kannst. Jeder Compiler auf diesem Planeten, der über das Prototypen Stadium hinaus ist, wird dir eine Division durch 2 durch einen Shift nach rechts ersetzen, WENN die Shift Operation auf der Zielhardware tatsächlich schneller ist. Das ist nichts worüber man sich Gedanken machen müsste. Als Programmierer wählt man die Operation so, dass sie für einen Menschen die klarste Ausdrucksweise darstellt und der Compiler macht den Rest. > also das ein shift um 2 nach rechts die zahl halbiert weis > ich, warum da aber auch nach 5 rechts geshiftet wird konnt ich mir auch > nicht erklären. das ist eine Division durch 32. Diese Sequenz eis5=lux>>1; eis5+=lux>>2; eis5+=lux>>5; ist identisch zu eis5 = lux / 2 + lux / 4 + lux / 32; nun ist das verständnismässig wahrscheinlich auch noch nicht klarer, warum das hier passiert, aber es ist zumindest ein Schritt in die Richtung, den Leser auf die Spur zu bringen. Das ist irgendeine Approximation einer Division, ohne tatsächlich Dividieren zu müssen. 1/2 + 1/4 + 1/32 ergibt 1 / 1.28 Jetzt musst du in deinem Datenblatt weitersuchen, wo eine Division durch diesen Wert (oder ein Wert der sehr nahe an dem liegt) zur Umrechnung von Werten gebraucht wird > Ist das schonmal richtig das geprüft wird ob die Zahl größer als 0x07FF > ist? Sieht gut aus. Das steht auch hier have to introduce an exponent of 2^1, since 3000 > 2047: 2047 ist 0x07FF > denn dann müsste der exponent erhöht werden und die zahl kleiner > sein. Ganz genau. Jedesmal wenn du den Exponent um 1 erhöhst, halbiert sich die Mantisse. 25 * 2 hoch 0 = 25.0 * 1 = 25 ist dasselbe wie 12.5 * 2 hoch 1 = 12.5 * 2 = 25 was wiederrum identisch ist zu 6.25 * 2 hoch 2 = 6.25 * 4 = 25 3.125 * 2 hoch 3 = 3.125 * 8 = 25 etc. Wie ich weiter oben schon gesagt habe: Ich würde da nicht großartig künsteln, sondern die Teile einzeln berechnen ohne zunächst auf die Bitpositionen zu schauen.
1 | Mantisse = Wert; |
2 | Exponent = 0; |
3 | |
4 | while( Mantisse > 2047 ) { |
5 | Mantisse /= 2; |
6 | Exponent += 1; |
7 | }
|
8 | |
9 | Ergebnis = ( Mantisse & 0x07FF ) | // zur Sicherheit nochmal beschneiden |
10 | ( ( Exponent & 0x0F ) << 10 ) | |
11 | ( SignBit << 15 ) |
Ums Vorzeichen musst du dich noch kümmern.
Andreas Fritsche schrieb: > Christian Hohmann schrieb: >> Leider passt der Code noch nicht richtig. Bei der Zahl 450000 kommt nach >> der obigen Rechnung 7D5D raus, aber richtig sollte sein 46DD. > > Heißt das, das Programm liefert 7D5D? Wieso sollte 46DD richtig sein? > > Ich erhalte von Hand für 450000 den Wert 7D5D. > 46DD entspräche dann 4497,92. Jetzt wird mir langsam alles klar: Dein Programm tut tatsächlich das richtige. Du willst doch gar nicht 450000 umwandeln sondern 4500. Dazu wird der Wert zuerst durch 1.28 dividiert, d.h. mit 100 multipliziert (wie in der Spezifikation gefordert) und durch 128 dividiert (damit die Zahlen klein bleiben, sonst müsste man 32bit-Arithmetik werwenden). Wegen der Division (128=2^7) fängst du mit dem Exponenten 7 an. Also ist doch alles in Ordnung. Da die Auflösung dieses Formates in diesem Bereich 2.56 beträgt, werden die 4500 genau wie meine obige 4497,92 zum selben Wert 46DD quantisiert. Erst bei 4500.48 wird der nächste Wert 46DE angenommen.
Karl heinz Buchegger schrieb: > > Grundsätzlich. > Schreib mathematische Sachen so hin, wie DU das gut lesen kannst. Jeder > Compiler auf diesem Planeten, der über das Prototypen Stadium hinaus > ist, wird dir eine Division durch 2 durch einen Shift nach rechts > ersetzen, WENN die Shift Operation auf der Zielhardware tatsächlich > schneller ist. Das ist nichts worüber man sich Gedanken machen müsste. > Als Programmierer wählt man die Operation so, dass sie für einen > Menschen die klarste Ausdrucksweise darstellt und der Compiler macht den > Rest. Nun ja, ich habe leider die leidvolle Erfahrung gemacht, dass manche Compiler für uC das NICHT können. Da muss man dann auch mal eine Berechnung in einzelne Statements zerlegen (z.B. CC5X). > > Ums Vorzeichen musst du dich noch kümmern. Das ist ja kein Problem mehr:
1 | ...
|
2 | while (eis5 > 0x07FF) |
3 | { // Exponent erhöhen falls Mantisse zu groß |
4 | eis5=eis5>>1; |
5 | exponent+=0x0800; |
6 | }
|
7 | if(negativ) |
8 | eis5=0x8800-eis5; // Mantisse invertieren |
9 | eis5+=exponent; |
10 | |
11 | ...
|
Ich habe hier die (offenbar falschen) Stellen, an denen eis5lux stand, geändert.
Andreas Fritsche schrieb: >> Als Programmierer wählt man die Operation so, dass sie für einen >> Menschen die klarste Ausdrucksweise darstellt und der Compiler macht den >> Rest. > Nun ja, ich habe leider die leidvolle Erfahrung gemacht, dass manche > Compiler für uC das NICHT können. Das muss dann aber ein sehr mieser Compiler sein. Eine derartige Umformung hat bereits der erste C-Compiler gemacht, den ich vor 25 Jahren benutzt habe. > Da muss man dann auch mal eine > Berechnung in einzelne Statements zerlegen (z.B. CC5X). Dann ist er allerdings wirklich mies. > Dazu wird der Wert zuerst durch 1.28 > dividiert, d.h. mit 100 multipliziert (wie in der > Spezifikation gefordert) und durch 128 dividiert (damit die > Zahlen klein bleiben, sonst müsste man 32bit-Arithmetik werwenden). > Wegen der Division (128=2^7) fängst du mit dem Exponenten 7 an. (klatsch. Flache Hand auf die Stirn) Aber natürlich! So erklärt sich auch der Exponent von 7. Der hat mich schon sehr verwirrt. (Alles in allem, ist das ein Zusammenhang, der sich einen Kommentar im Code verdient hätte)
Karl heinz Buchegger schrieb: >> Da muss man dann auch mal eine >> Berechnung in einzelne Statements zerlegen (z.B. CC5X). > > Dann ist er allerdings wirklich mies. Naja, ist eben die (kostenlose) Light-Version des CC5X-Compilers. Mit dem hatte ich meine ersten Gehversuche mit uCs (PIC12F509) gemacht. Ich meine mich aber zu erinnern, dass er bei Multiplikation mit Konstanten selber shiftet und addiert. Division durch 1.28 hätte er jedenfalls nicht hingekriegt (aber auch kein anderer Compiler ;-) > (klatsch. Flache Hand auf die Stirn) > Aber natürlich! So erklärt sich auch der Exponent von 7. > Der hat mich schon sehr verwirrt. Ja, aber ich habe nochmal drüber nachgedacht: Bei Zahlen unter 1310.72 liefert das Programm ungenaue Ergebnisse, da das Programm keine Exponenten unter 7 erzeugen kann, wodurch am Ende Bits fehlen. Es wäre ganz schön, wenn der OP uns sagen könnte, welchen Datentyp er für "lux" und "eis5" verwendet, denn der Mamimalwert von 670760 passt in 16 Bits ja auch nicht rein.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.