Guten Morgen an dei Spezialisten! Habe eine Frage ich muß mehrere Integer und Long Variablen mit Float Korrekturfaktoren multiplizieren. Z.B. ADC Wert 532 (10bit) Referenzspannung 4.096V Faktor 102,2044 usw. Dann kommen noch verschiedene Zeitberechnungen hinzu! Wie kann man solche Berechnungen durchführen nur mit Int oder Long?
Überlege dir, welcher Bruch deiner Floatzahl möglichst nahe kommt. 102,2044 ist ziemlich genau 14002/137. Also erst mit 14002 multiplizieren, dabei überlegen,ob ein Überlauf stattfinden kann und eventuell auf 32 Bit erweitern, dann durch 137 teilen. Eine Tabellenkalkulation kann beim suchen der Faktoren helfen.
Du näherst die float-Zahlen durch Brüche an, deren Nenner ne Potenz von zwei ist, also z.B. 102.2044 ~ 6541/2^5. Die Division durch ne Zweierpotenz wird durch nen einfaches Rechtsschieben des Integerwertes realisiert. Die erreichbare Genauigkeit wird durch die Anzahl der Bits betsimmt, die Du zur Verfügung hast. Aus ähnlichem Grund vermutlich die Referenzspannung von 4.096V: 4096 = 2^12. Cheers Detlef
Wenn der Ausgangswert nur 10-Bittig ist, dann ist 102,2044 Mumpitz, 102,2 reicht vollkommen. 102,2 ist z.B. 511/5 Peter P.S.: Du weißt, daß der Endwert bei 10Bit 4,096V / 1024 * 1023 = 4,092V ist ?
Wenn Du keine Einschränkungen bezüglich des Compilers/Codegröße hast, dann rechne mit 'float'. Du gibst den Faktor ein, den Du brauchst, brauchst dafür nicht zu denken und keine Denkfehler zu machen. Insbesondere, wenn Deine Faktoren variabel sind, ist es Dein Programm dann auch.
Danke für die schnellen Antworten. Bin relativ neu in diesem Bereich und wurde darauf hingewiesen das Flot Berechnungen unheinlich viel Rechenzeit für einen Controller bedeuten. Der Ratschlag war besser 5 mal Integer berechnen wie einmal Float.
Float frisst auch viel Speicherplatz. Das ist oft schlimmer als die Rechenzeit. Sobald eine Float-Rechnung im Programm auftaucht, wird die Float-Bibliothek dazugelinkt, probier einfach mal aus, wieviel Platz das kostet.
<<wurde darauf hingewiesen das Flot Berechnungen unheinlich viel Rechenzeit für einen Controller bedeuten. Der Ratschlag war besser 5 mal Integer berechnen wie einmal Float.>> Alles dummes Zeug ! Bei diesen Ratschlägen muß man zurückschlagen.
Michael, macht es Spaß, Neulinge mit solchem Mist zu verunsichern? Tatsache bleibt: 1. Auch float-Benutzung schützt nicht vor Denkfehlern 2. Float braucht mehr Speicher und Rechenzeit als Fixkomma 3. Aufgaben der realen Welt kann man fast immer mit Fixkomma erledigen
@Philipp: Ich mag es einfach nicht, wenn Vorurteile aus der Programmiersteinzeit immer wieder unbewiesen behauptet werden. Für heutige µPs ist es kein Problem mit float effektiv und schnell umzugehen. Gerade ein Anfänger sollte lernen, die Technik zu nutzen und nicht dazu gebracht werden, im Bitgeschupse zu ersticken. Wer es mag, kann ja seinen Taschenrechner auf 'ganzzahlig' einstellen, womöglich um Zeit oder Strom zu sparen; aber so blöd wird doch niemand ernsthaft sein.
Michael, float benoetigt mehr Ressourcen und ist langsamer als integer, das ist eine Tatsache. State of the art Signalverarbeitung wie handy, DSL etc laeuft auf integer Prozessoren, nicht auf float DSPs. Wenn Du Strom sparen muss wg Batterie und hohe peformance in nem embedded Geraet brauchst ist integer die Wahl. Float kannst Du gut auf Deinem Pentium machen, der 60W zieht oder so. Verschwendung von Ressourcen ist ja modern, damit die Prozessorhersteller immer schnellere Prozessoren und die Plattenhersteller immer groessere Platten verkaufen koennen, manchmal geht das aber nicht, wenn Strom oder Platz fehlt. Wenn man schon fette Prozessorleistung hat, kann man die auf clevere Dinge verwenden, als sie ueber floatrechnung zu verbraten. Cheers Detlef
mir scheint, du bist eher ein Theoretiker oder max. praktischer PC-Programmierer. In der MC-Welt hat man immer mit knappen Ressourcen zu kämpfen, es sei denn, man überlädt sein Projekt mit Rechenmonstern. Das kann man gerne machen, wenn es was hübsches fürs Bastelstübchen oder ein Vorzeigeprojekt sein soll. Gehts in die Produktion und will man damit am Ende was damit verdienen (ja, es gibt Leute, die machen das), baut man keinen Blackfin ein, um eine Temperatur zu messen. Ich habe schon Unmengen von Projekten realisiert, und oft genug sind sehr zeitkritische Sachen dabei. Der Grossteil davon läuft mit Mega8 und Mega32, float habe ich noch NIE eingesetzt. Ich denke inzwischen kaum noch nach, ob es möglich wäre, float zu benutzen.
Wie ich sehe, werden die Vorurteile unüberlegt wiederholt. Nach meinen Tests auf einem ATmega mit 16 MHz brauchen eine Multiplikation als 'long' oder als 'float' jeweils ca. 20-30µs. Dabei werden die MUL-Befehle nicht verwendet. Bei FP wird der Code um ca. 800 Byte erhöht. Daß die Codegröße explodiert, oder der Prozessor in scheinbaren Schlaf verfällt, ist bei FP nicht zu erkennen. Jeder kann sich durch eigene Tests selbst ein Bild machen. Und der Umstand das jemand kein 'float' braucht, kann nicht bedeuten, daß nun niemand es gebrauchen darf. Profiprogrammierer werden sicherlich feststellen, daß Multiplikation schneller als Division abläuft. Folgt daraus nun, daß man keine Divisionen mehr machen darf ?
hallo, ich möchte eine festkommazahl per hand einfacher umrechnen z.B: bsp.: 0.73 wertigkeit 2^-1 0.5 passt in 0.73 rest 0.23 bit = 1 wertigkeit 2^-2 0.25 passt nicht in 0.23 rest 0.23 bit = 0 wertigkeit 2^-3 0.125 passt in 0.23 rest 0.105 bit = 1 wertigkeit 2^-4 0.0625 passt in 0.105 rest 0.0425 bit = 1 entspricht also: 0.b... in der hexadezimalen darstellung oder 0.1011... in binärer darstellung. kann ich die nachkommastellen auch mit einem taschenrechner, der dezimal/hex/binär umrechnen kann, allerdings ohne komma, also als volle zahl umrechnen!? komme nicht auf die lösung... maddin
Mal ne Frage an den Spezialisten: Warum werden die MUL Befehle denn nicht benutzt? Wo sie doch schon mal (ebenso wie die FP-Libs) da sind und das ganze vermutlich auch beschleunigen würden.
>>Warum werden die MUL Befehle denn nicht benutzt?>> Ganz einfach, es sind LIBs für alle AVRs, die nicht alle MUL kennen. Die Routinen laufen daher auch noch auf den AT90Sxxxx. Könnte man mal anpassen, aber 'float' ist ja schnell genug :-) Gibt es vielleicht auch Leute, die 'float' einsetzen und eine Einschätzung pro/contra geben können ? Empfehlungen von Programmieren, die das nie gemacht haben, bringen einen doch nicht weiter.
@Maddin: die 0.73 mit 2^4 multiplizieren und dann mit nem Taschenrechner in binär umwandeln. @Michael: Kein Wunder, daß Deine float MUL genauso lange dauert wie Deine integer MUL, wenn die nativen Multiplikationsfkt. des Prozessors nicht genutzt werden. >>Folgt daraus nun, daß man keine Divisionen mehr machen darf ? << In schnellen Echtzeitanwendungen sind Divisionen, auch integer, tödlich. Die werden mit multiplizieren/Schieben gemacht. >>Und der Umstand das jemand kein 'float' braucht, kann nicht bedeuten, daß nun niemand es gebrauchen darf.<< Wehe Dir, wenn ich Dich nochmal bei ner float-Rechnung erwische! Cheers Detlef
0.73 mal 16 = 11,68 11 entspricht b - scheiße - peinlich - hab mich durch die komma rechnung allgemein total blenden lassen... danke
> Warum werden die MUL Befehle denn nicht benutzt?
Das kann ich gar nicht glauben.
Bsp: avr-gcc, aktuelle Version, ATMega16, Optimierung auf Os
j = 58 * i;
aa: 89 81 ldd r24, Y+1 ; 0x01
ac: 9a 81 ldd r25, Y+2 ; 0x02
ae: 2a e3 ldi r18, 0x3A ; 58
b0: 30 e0 ldi r19, 0x00 ; 0
b2: 82 9f mul r24, r18
b4: a0 01 movw r20, r0
b6: 83 9f mul r24, r19
b8: 50 0d add r21, r0
ba: 92 9f mul r25, r18
bc: 50 0d add r21, r0
be: 11 24 eor r1, r1
c0: 5c 83 std Y+4, r21 ; 0x04
c2: 4b 83 std Y+3, r20 ; 0x03
Da wird eindeutig ein mul benutzt.
Wenn ich die Konstante mal ändere:
j = 3 * i;
aa: 29 81 ldd r18, Y+1 ; 0x01
ac: 3a 81 ldd r19, Y+2 ; 0x02
ae: c9 01 movw r24, r18
b0: 88 0f add r24, r24
b2: 99 1f adc r25, r25
b4: 82 0f add r24, r18
b6: 93 1f adc r25, r19
b8: 9c 83 std Y+4, r25 ; 0x04
ba: 8b 83 std Y+3, r24 ; 0x03
Hier optimiert der gcc, da er eine Multiplikation mit 3
durch 1 * Kopiern und 2 Additionen schneller realisieren kann.
Nach dem Muster 3 * i <==> ( i + i ) + i
Wers nicht glaubt, hier ist das komplette Program.
Der Mambo-Zambo mit der Hilfsfunktion dient nur dazu,
den Compiler an einer Constant-folding Optimierung zu
hindern:
<c>
void foo( int* a )
{
*a = 5;
}
int main()
{
int i;
int j;
foo( &i );
j = 5 * i;
foo( &j );
while( 1 )
;
}
</c>
Ach herje, Michael redet ja gar nicht von int, sondern von long Multiplikation. Also das Ganze nochmal, diesmal mit long: <C> j = 58 * i; b2: 89 81 ldd r24, Y+1 ; 0x01 b4: 9a 81 ldd r25, Y+2 ; 0x02 b6: ab 81 ldd r26, Y+3 ; 0x03 b8: bc 81 ldd r27, Y+4 ; 0x04 ba: bc 01 movw r22, r24 bc: cd 01 movw r24, r26 be: 2a e3 ldi r18, 0x3A ; 58 c0: 30 e0 ldi r19, 0x00 ; 0 c2: 40 e0 ldi r20, 0x00 ; 0 c4: 50 e0 ldi r21, 0x00 ; 0 c6: 0e 94 70 00 call 0xe0 <__mulsi3> ca: dc 01 movw r26, r24 cc: cb 01 movw r24, r22 ce: 8d 83 std Y+5, r24 ; 0x05 d0: 9e 83 std Y+6, r25 ; 0x06 d2: af 83 std Y+7, r26 ; 0x07 d4: b8 87 std Y+8, r27 ; 0x08 </C> Da wird tatsächlich eine Funktion aufgerufen. Was macht die? <C> 000000e0 <__mulsi3>: e0: 62 9f mul r22, r18 e2: d0 01 movw r26, r0 e4: 73 9f mul r23, r19 e6: f0 01 movw r30, r0 e8: 82 9f mul r24, r18 ea: e0 0d add r30, r0 ec: f1 1d adc r31, r1 ee: 64 9f mul r22, r20 f0: e0 0d add r30, r0 f2: f1 1d adc r31, r1 f4: 92 9f mul r25, r18 f6: f0 0d add r31, r0 f8: 83 9f mul r24, r19 fa: f0 0d add r31, r0 fc: 74 9f mul r23, r20 fe: f0 0d add r31, r0 100: 65 9f mul r22, r21 102: f0 0d add r31, r0 104: 99 27 eor r25, r25 106: 72 9f mul r23, r18 108: b0 0d add r27, r0 10a: e1 1d adc r30, r1 10c: f9 1f adc r31, r25 10e: 63 9f mul r22, r19 110: b0 0d add r27, r0 112: e1 1d adc r30, r1 114: f9 1f adc r31, r25 116: bd 01 movw r22, r26 118: cf 01 movw r24, r30 11a: 11 24 eor r1, r1 11c: 08 95 ret </C> Na also, geht doch. Wunderbare mul da drinnen
Interessant, wieviel Hellseher es hier doch gibt, die wissen, welchen MC und welchen Compiler Stephan benutzt. Float generell zu verdammen ist generell falsch. Bei meinem Frequenzmesser aufm AT89C2051 benutze ich auch float. - für 5 Digits ist die Genauigkeit ausreichend. - für menschliche Ausgaben ist float mindestens 100 mal zu schnell. - der Timerauslesekram ist nur wenige 100 Bytes groß, da ist noch reichlich Platz in 2kB Flash für float (das ganze Programm benötigt 1670Byte). Es gab also nicht den geringsten Grund float nicht zu nehmen. Peter
> >>Warum werden die MUL Befehle denn nicht benutzt?>> > > Ganz einfach, es sind LIBs für alle AVRs, die nicht alle MUL kennen. > Die Routinen laufen daher auch noch auf den AT90Sxxxx. Oder redest du von selbst erstellten Libraries? Na, ja. Wenn man den Mega mit angezogener Handbremse fährt ...
hm, Genauigkeit float/integer: Nen 32Bit float ist nicht 'genauer' als nen 32Bit Integer. Bei ner ernsthaften Integerrechnung ist ne genaue Betrachtung der Fehler genauso notwendig wie bei ner Floatrechnung. Die 32 Bit Floats bringen dir lediglich ne größere Dynamik als die 32 Bit integer, die 'Genauigkeit' (dazu gehört allerdings ne Definition) ändert sich nicht. Cheers Detlef
Wenn ich das 'mul long' Beispiel von Karl-Heinz auszähle, komme ich auf etwa 40 Taktzyklen. Bei 16MHz AVR sind das dann 3,5µs. Implementiert man 'float' mit MUL-Befehlen, reicht eine 24x24Bit Routine, die etwas schneller sein wird, und es wird eine Skalierung des Exponenten notwendig, sodaß ich eine 'fmul' auf ca. 5µs Schätze, wenn sie gut codiert ist. Greife ich den Vorschlag von Detlef_A auf (102.2044 ~ 6541/2^5), so sieht man, daß man mit 'int' nicht mehr hinkommt, ohne einen Überlauf zu erhalten (10 bit Wert * 6541). Und bei dieser Optimierung wird von positiven Werten ohne Rundung des Ergebnisses ausgegangen. Rechnet man hingegen gleich mit 'float' reicht anstatt <Wert * Faktor/Divisor> die einmalige Berechnung eines Skalierungsfaktors und anschließend <Wert * Skal-Faktor>. Bei dieser Berechnung ist das Ergebnis automatisch vorzeichenrichtig gerundet. Ausgehend von Stephans Anforderungen rate ich weiterhin zur Verwendung von 'float', selbst wenn er 1000 Berechnungen/s durchführen muß. Und wenn eines Tages ein Nachfolger seine Programme warten muß, kann dieser dann einfach die Konstante 102.2044 durch 103.3309 ersetzen, weil sich z.B. der ADC-Eingangsbereich verändert hat.
> 40 Taktzyklen
Na ja. Bei dem was der OP gepostet hat, braucht er kein long.
Da kommt er mit int locker durch. Das sind dann keine 40 Zyklen
sondern wir sind bei (ich habs nicht nachgezäht) wesentlich
weniger.
Und ob man eine float Routine in 70 bis 80 Zyklen hinkriegt
weis ich nicht. Denke aber eher nicht.
Aber im Prinzip geb ich dir recht. Wenn ich Zeit genug habe
und im Speicher nicht sparen muss dann spricht nichts gegen
float. Es sei denn man kommt mit der Rechengenauigkeit nicht hin.
Das ist aber bei einer simplen einfachen Multiplikation eher
unwahrscheinlich. Wenn die Genauigkeit bei komplexen
Berechnungen nicht reicht, dann hat man allerdings sowieso andere
Probleme als auf die Laufzeit zu achten.
Ihr wisst ja: ich bin ebenfalls ein Int-Fan. Zum Vergleich 32-bit-Int vs. 32-bit-Float: ganz klar ist die Int-Version genauer, da hier alle Bits für das Ergegnis zu Verfügung stehen, bei Float nur 23. Die Kunst beim Int-Rechnen ist das Skalieren. Tips wie: "102,2044 ist ziemlich genau 14002/137. Also erst mit 14002 multiplizieren, dabei überlegen,ob ein Überlauf stattfinden kann und eventuell auf 32 Bit erweitern, dann durch 137 teilen." oder "511/5" (Tut mir leid, Peter, dass wir beide uns immer mal wieder mal "in die Quere kommen") bezeugen, dass der Poster sich noch ein wenig mit Zahlenformaten beschäftigen sollte. Die richtige Lösung lautet, wie von Autor: Detlef _A (Detlef_A) Datum: 14.09.2006 10:33 bereits vorgeschlagen: skaliere so, dass die Wertebereiche maximal ausgeschöpt sind und dass das Ergebnis in einem Format ist, mit dem man am einfachsten (möglichst ohne Schieben) weiterarbeiten kann. z.B. Du willst eine 10-bit gewandelte Spannung (Vref=4,096) mit vier Nachkommastellen anzeigen, multiplizierst Du 532 (entspricht 2,1280V max. 1023) mit 40, um auf 21280 zu kommen, und nach der ersten Stelle schmugglest Du ein Komma in die Anzeige - na klar: bei 'gerader' Vref braucht man nicht viel rechnen (schlechtes Beispiel). Besseres Beispiel: Vref=3,3V 10bitADC Faktor ist 33000/1024=32,2265625 (7 bit) 24bit-Float-Rechnung, Skalieren des Faktors mit 2^8 (ist ausreichend genau und hat den Vorteil, dass das Ergebnis nicht geschoben wird) 32,2265625*2^8=8250 532(entspricht 532/1024*3,3V=1,714V)*8250=4389000 (10+14=24, muss also 16+16=32 bit mul sein) 4389000/256(letztes Byte abzwacken = nur mittlere 16 Bits verwenden)=17144,53125 17144 / 10000 (Komma nach der 1.Stelle reinmalen)=1,7144 (Spannung) Nehmen wir Dein Beispiel: Du willst mit 532 (10 bit) mit 102,2044 (eigentlich 7 Bit) multiplizieren (54372,7408). Du brauchst trotzdem 17 Bits, weil das maximale Ergebnis 1023*102,2044=104555,1012 (17 bits) sein kann. Das ist recht ungünstig, und man sollte sich überlegen, ob es möglich ist, den Umrechnunsfaktor auch zu skalieren, nämlich mit 0,5 , um auf 16 Bits zu kommen. Später musst Du das Ergebnis mit dem Faktor 2 korrigieren. Wie gesagt, es kommt sehr stark auf die Aufgabe an, die man als 'ein Ganzes' sehen muss (Wertebereiche - Genauigkeit), um zu entscheiden, ob solche Tricks möglich sind. Auch die Wahl der Referenzspannung spielt bei dieser Entscheidung mit eine Rolle: Bei der ursprünglichen Aufgabe wäre eine Möglichkeit, die Vref auf 4,0076 einzustellen und mit 100 statt 102,2044 zu multiplizieren, oder auf Vref= 3,130 und Ergebis mal 128 (ein Byte anhängen und einmal nach rechts schieben). Dazu braucht man alle Angaben zur Aufgabe (max. Eingangsspannung). Wobei sich die Frage stellt, ob man die ganze Umrechnerei besser direkt (ohne Umweg über skaliertes Zwischenergebnis) macht, anstatt bei der Anzeigeroutine wieder mit 'durch 10 Teilen' Klimmzüge machen muss. Ähnliche Threads: http://www.mikrocontroller.net/forum/read-1-409044.html http://www.mikrocontroller.net/forum/read-1-356561.html#356621 http://www.mikrocontroller.net/forum/read-1-343046.html http://www.mikrocontroller.net/forum/read-1-288423.html
Korrigieren: "auf Vref= 3,130 und Ergebis mal 128 (ein Byte anhängen und einmal nach rechts schieben)." --> auf Vref= 2,5600V und Ergebis mal 64 (ein Byte anhängen und zweimal nach rechts schieben). 2,128V = 532,000 bei Vref=4,0960V 532*102,2044=54372,7408 2,128V = 543,735 bei Vref=4,0076V 544*100,0000=54400,0000 2,128V = 696,189 bei Vref=3,1300V 696*128,0000=89088,0000 (Fehler) 2,128V = 849,573 bei Vref=2,5640V 850* 64,0000=54400,0000
@Profi "oder "511/5" (Tut mir leid, Peter, dass wir beide uns immer mal wieder mal "in die Quere kommen")" Damit kommen wir uns doch nicht in die Quere, ist ja genau das gleiche, was ich bereits im 4.Posting geschrieben hatte. Peter
"Zum Vergleich 32-bit-Int vs. 32-bit-Float: ganz klar ist die Int-Version genauer, da hier alle Bits für das Ergegnis zu Verfügung stehen, bei Float nur 23." @Profi: ich stimme Dir ja in vielen Dingen zu, hier habe ich aber meine Schwierigkeiten. In der realen Welt könnte es ja mal vorkommen, daß ein int-Wert mit 1/82357 skaliert werden muß. Auf die Schnelle würde ich sagen, da bleiben dann im Ergebnis nur noch 16 Bit genau. Um ehrlich zu sein, früher habe ich die Werte auch so verschoben, um float-Rechnerei zu sparen (8051 12MHz). Da gab es einen Sinn. Wenn ich heute (oben) sehe, welche Tricksereien gemacht werden sollen, um ein paar hundert Byte Code und sagen wir mal 1% CPU-Leistung einzusparen, muß ich schmunzeln. Wenn alle Stricke reißen, kann man immer noch optimieren. Aber ich glaube niemanden, der erzählen will, wegen Anwendung von 'float' jemals in zeitliche Bedrängnis gekommen zu sein, oder den Code nicht mehr in den Speicher bekommen zu haben. @Stephan: Sag mal Bescheid, wie Du Dich entschieden hast.
@Michael Du sprichst mir aus der Seele! Ja, wo sind denn die vielen Bits Int-Genauigkeit, wenn ich schon bei einer einfachen Konstantenformel herumknobele, ob ich das mal-1000 noch vor dem durch-365 machen lasse (weil genauer) oder lieber hinterher (weil besser leserlich) ... Ansonsten breche ich jetzt mal eine Lanze für die 32bit-floating-point-Arithmethik - so schlecht ist die nämlich gar nicht. Rückgerechnet auf 16bit integer (lassen wir mal die Kirche im Dorf) bringt sie allemal auf 1 LSB genaue Resultate, selbst wenn man zwischendurch durch einen >16bit-Wert teilt (82357!). Und wer meint, für eine genaue Berechnung keine Zeit zu haben, mag sich halt mit seinen Hausnummern herumschlagen und die mit noch mehr Aufwand soweit egalisieren. Oder sich mal mit einer SPS beschäftigen, die ihre Programmzyklen gar in ms zählt :)
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.