Hallo zusammen, ich arbeite hier mit einem Atmega2560 und Ganzzahlen, die, bedingt durch ihre Größe, in Mantisse und Exponent geteilt sind. Im Wesnentlichen ist es ein Zähler, der auf bestimtme Triggerbedingungen intern zählt, mir aber eben das o.g. Zahlenformat liefert. Sprich, es gibt nur ganze Zahlen! Nun, ich erhalte die beiden Teile und rechne mit ldexp(m,e) und erhalte meinen Wert. Mit sprintf() bzw. dtostrf() lässt sich sogar ein string-Abbild der Zahl realisieren. D.h. dieses char-Array kann ich jetzt zB per UART verschicken etc. Das Problem: Vergleiche ich die Berechnungen zwischen PC und µC, stelle ich fest, dass nach 8 VORkommastellen gerundet wird und mit '0' aufgefüllt wird. Absolut heißt das für mich, dass es keine Rundung, nicht mal eine Schätzung ist, sondern schlichtweg falsch. Beispiel: Rechner: Mantisse: 216, Exponent: 60, ldexp(Mantisse, Exponent): 249031044995078946816 µC: Mantisse: 216,Exponent: 60, ldexp(Mantisse, Exponent) 249031050000000000000 Nach einer Suchmachinen-Recherche las ich was von, dass avr-gcc intern mit float-Genauigkeit rechnet. Ist dem so? Besteht eine Möglichkeit die Genauigkeit weiter zu erhöhen? Genauer gesagt, kann man die Rundung "später", z.B. nach 30 Vorkommastellen "einstellen"? Natürlich besteht tech. die Möglichkeit die beiden Teile an den Rechner zu schicken, der seinerseits die Berechnung übernimmt. Das ist aber so nicht konzipiert. Berechnungszeit spielt hierbei erstmal keine Rolle: es können schon mal Sekunden anfallen, wenn notwendig. Danke und viele Grüße
Ein 8 Bit µC ist nun mal nicht dafür gebaut, mit Arbitrary Precision Arithmetik umgehen zu können. Bei welcher Aufgabenstellung kommen denn derart große Zahlen vor? Aber: Wenn du das unbedingt brauchst - das Stichwort 'Arbitrary Precission Arihtmetik' ist ja schon gefallen.
:
Bearbeitet durch User
kyrel schrieb: > Nach einer Suchmachinen-Recherche las ich was von, dass avr-gcc intern > mit float-Genauigkeit rechnet. Ist dem so? Ja. Und float hat nun mal nur 8 signifikante Stellen. Da ist es sinnvoll, die hinteren Stellen wegzurunden... > Besteht eine Möglichkeit die Genauigkeit weiter zu erhöhen? Nicht so ohne Weiteres. Aber du kannst dein eigenes Zahlenformat definieren und eigene Rechenfunktionen dafür schreiben.
yo, die float beim AVR haben 32 Bit entsprechend 6 signifikanten Stellen. Hier ist eine 64Bit float Emulation von mir: Beitrag "64 Bit float Emulator in C, IEEE754 compatibel" das macht 15 signifikante Stellen. Cheers Detlef
:
Bearbeitet durch User
Nur wenn es es um ganze Zahlen geht verstehe ich nicht, warum man überhaupt mit Exponent und Mantisse, sprich mit Gleitkommazahlen arbeiten muss? Wenn die Integerdatentypen des Compilers zu klein sind, definiere ich mir eben selbst einen Typ.
1 | #if ( !defined(FALSE) && !defined(TRUE) ) |
2 | #define FALSE 0 |
3 | #define TRUE !FALSE |
4 | #endif |
5 | |
6 | typedef bignumber[16] uint128; |
7 | typedef unsigned char bool; |
8 | |
9 | bool add ( uint128 *z1, uint128 *z2) |
10 | { |
11 | bool result = TRUE; |
12 | |
13 | //Add Implementation |
14 | |
15 | return(result); |
16 | } |
17 | |
18 | void main(void) |
19 | { |
20 | uint128 z1,z2; |
21 | |
22 | if( !add(&z1,&z2) ) |
23 | { |
24 | //Fehlerbehandlung |
25 | } |
26 | } |
Natürlich muss man dann die Funktionen für die notwendigen Operatoren, hier add(), selbst schreiben. Zum Beispiel könnte man die Funktion add() mit Hilfe des Inline-Assemblers schreiben. Das wäre dann eine fortgesetzte byteweise Addition unter Berücksichtigung des Carry-Flags als eventuell auftretender Übertrag.
Ok z1 und z2 sollten vor der Addition natürlich sinnvolle Werte zugewiesen werden. So ist das Ergebniss undefiniert. :-)
grr die definition des interegertyps war verkehrt
1 | typedef unsigned char uint128[16]; |
kyrel schrieb: > ich arbeite hier mit einem Atmega2560 und Ganzzahlen, die, bedingt durch > ihre Größe, in Mantisse und Exponent geteilt sind. Das sind dann aber keine Ganzzahlen mehr, sondern Fließkommazahlen. > Im Wesnentlichen ist > es ein Zähler, der auf bestimtme Triggerbedingungen intern zählt Und wer ist auf die ausgesprochen blöde Idee gekommen, einen Zähler als Fließkomma-Variable zu implementieren? > Das Problem: Vergleiche ich die Berechnungen zwischen PC und µC, stelle > ich fest, dass nach 8 VORkommastellen gerundet wird und mit '0' > aufgefüllt wird. > > Absolut heißt das für mich, dass es keine Rundung, nicht mal eine > Schätzung ist, sondern schlichtweg falsch. Herzlichen Glückwunsch! Du hast gerade den Unterschied zwischen exakter Ganzzahlarithmetik und aproximativer Fließkommaarithmetik entdeckt. OK, du bist nicht der Erste ... > Besteht eine Möglichkeit die Genauigkeit weiter zu erhöhen? Genauer > gesagt, kann man die Rundung "später", z.B. nach 30 Vorkommastellen > "einstellen"? Jeder Computer kann im Prinzip mit beliebig langen Ganzzahlen rechnen. Natürlich nicht von Haus aus, aber man kann die entsprechenden Algorithmen programmieren. Und natürlich haben das schon vor dir Leute getan. Man braucht das z.B. für Crypto-Algorithmen. ----- Karl Heinz schrieb: > Ein 8 Bit µC ist nun mal nicht dafür gebaut, mit Arbitrary Precision > Arithmetik umgehen zu können. Er ist nicht mehr oder weniger dafür gebaut als ein PC mit 64-bit CPU. Wenn man mit z.B. 2048-bit Zahlen rechnen muß, dann ist es relativ unerheblich, ob man die in 64-bit- oder 8-bit-Häppchen unterteilt. Der einzige Unterschied ist am Ende die benötigte Rechenzeit. ----- @kyrel: Wieviel Dezimalstellen brauchst du denn? Und welche Rechnenoperationen? Wenn es wirklich nur darum geht, eine Zahl zu inkrementieren, dann kann man die "lange" Zahl einfach als Array kurzer Zahlen implementieren und kümmert sich selber um den Übertrag:
1 | uint16_t zaehler[4] = {0, 0, 0, 0}; |
2 | |
3 | void hochzaehlen() { |
4 | for (uint8_t i=0; i<4; i++) { |
5 | zaehler[i]++; |
6 | if (zaehler[i] != 0) { |
7 | break; |
8 | }
|
9 | }
|
10 | }
|
Wenn man die Zahl auch noch dezimal ausgeben will, kommt man besser, wenn man in jedem der 4 Teile des Zählers nur bis 9999 zählt:
1 | void hochzaehlen() { |
2 | for (uint8_t i=0; i<4; i++) { |
3 | zaehler[i]++; |
4 | if (zaehler[i] > 9999) { |
5 | zaehler[i] = 0; |
6 | } else { |
7 | break; |
8 | }
|
9 | }
|
10 | }
|
11 | |
12 | void ausgabe() { |
13 | for (uint8_t i=0; i<4; i++) { |
14 | printf("%04d", zaehler[3-i]); |
15 | }
|
16 | }
|
Jeder Teil kann dann 4 Dezimalstellen halten. Für die Ausgabe braucht man die Teile nur in umgekehrter Reihenfolge und mit jeweils führenden Nullen hintereinander auszugeben. Mit ein bisschen Anstrengung kann man die führenden Nullen noch wegbekommen, aber das sollte ja auch nur eine Anregung sein. XL
Hallo und danke für die Antworten! Einige Stichworte sind ja schon gefallen, dem werde ich nachgehen. Zum Verständnis: a) Ich hole einen Zählerwert (von einem IC) ab, da diese eben sehr sehr gross werden können, hat der IC-Hersteller einfach diese Dinge in Mantisse und Exponent unterteilt! b) Dass es sich dabei - im Grunde - um ein Fließkommazahldarstellung handelt, ist mir klar, ich weiss aber, dass eben ganze Teile gezählt werden (können), also im Grunde nur XXX,0-Werte, eine Zahl wie X,3 würde in diesem Zusammenhang keinen Sinn ergeben. Ich habe jetzt also die Information der Zahl, gespeichert in Mantisse+Exponent, möchte aber die absolute Zahl, sagen wir mal auf einem LCD/UART/whatever darstellen, also berechne ich aus Mantisse+Exponent den Wert. Und genau hierbei stelle ich fest, dass eben die "standard"-Funktionen den absoluten Wert eben nicht berechnen können bzw. stark runden. Daher die Frage, ob unter einfachen/schnellen Mitteln die Genauigkeit "eingestellt" werden kann. - So wie ich das verstanden habe, existiert keine Option gemäß (Rundung=8, Rundung=16 etc.), sprich, selbst machen. Im Grunde will ich wissen, ob ich gedanklich richtig liege, oder ob ich noch eine "Kleinigkeit" übersehen habe, um schnell zum Ziel zu kommen. Nochmals danke und viele Grüße
kyrel schrieb: > a) Ich hole einen Zählerwert (von einem IC) ab, da diese eben sehr sehr > gross werden können, hat der IC-Hersteller einfach diese Dinge in > Mantisse und Exponent unterteilt! Was für ein IC und Hersteller ist das? > b) Dass es sich dabei - im Grunde - um ein Fließkommazahldarstellung > handelt, ist mir klar, ich weiss aber, dass eben ganze Teile gezählt > werden (können), also im Grunde nur XXX,0-Werte, eine Zahl wie X,3 würde > in diesem Zusammenhang keinen Sinn ergeben. Fließkommazahlen machen diesen Unterschied nicht. Sie speichern eine Zahl immer in der Form
1 | 0b0,1xxxxxxx * 2^0byyy |
xxxxxx sind die gespeicherten Bits der Mantisse. yyy die gespeicherten Bits des Exponenten. Die Mantisse ist (außer für 0) also immer eine Zahl zwischen aus dem Intervall [0.5 .. 1). Oder anders gesagt: die Normalisierung sorgt dafür, daß das erste Bit der Mantisse nach dem Komma immer eine 1 ist. Es wird daher nicht mitgespeichert. > Ich habe jetzt also die Information der Zahl, gespeichert in > Mantisse+Exponent, möchte aber die absolute Zahl, sagen wir mal auf > einem LCD/UART/whatever darstellen, also berechne ich aus > Mantisse+Exponent den Wert. Und genau hierbei stelle ich fest, dass eben > die "standard"-Funktionen den absoluten Wert eben nicht berechnen können > bzw. stark runden. Der Fehler ist, überhaupt diese Funktionen zu verwenden. Wenn du Fließkomma hast, dann wirf es printf als Fließkomma vor und sag daß du keine Nachkommastellen willst. Bis hin zur Länge der Mantisse wird das exakt. Danach prinzipbedingt nicht mehr (der Zähler scheint dann in Sprüngen größer 1 zu zählen). XL
kyrel schrieb: > ich arbeite hier mit einem Atmega2560 und Ganzzahlen, die, bedingt durch > ihre Größe, in Mantisse und Exponent geteilt sind. Im Wesnentlichen ist > es ein Zähler, der auf bestimtme Triggerbedingungen intern zählt, mir > aber eben das o.g. Zahlenformat liefert. Sprich, es gibt nur ganze > Zahlen! Das ist schlicht falsch. Er liefert Dir Fliesskommazahlen! Und die sind nicht mal sonderlich genau. > [...] > Das Problem: Vergleiche ich die Berechnungen zwischen PC und µC, stelle > ich fest, dass nach 8 VORkommastellen gerundet wird und mit '0' > aufgefüllt wird. So wird das bei float üblicherweise gemacht. > Absolut heißt das für mich, dass es keine Rundung, nicht mal eine > Schätzung ist, sondern schlichtweg falsch. Nein. Was falsch ist, die Deine Annhame zur Genauigkeit der vom Chip übermittelten Zahl. > Beispiel: > > Rechner: > Mantisse: 216, Exponent: 60, > ldexp(Mantisse, Exponent): 249031044995078946816 Hui, die Zahl passt ja nicht mal mehr in 64 Bits rein... Es ist echt unwahscheinlich, dass eine reale Messung auf genau diese Zahl gekommen ist. > µC: > Mantisse: 216,Exponent: 60, > ldexp(Mantisse, Exponent) 249031050000000000000 Was Du übersehen hast: Der Chip liefert Dir nicht die Info über die genaue Zahl, sondern nur dass sie im Bereich von 215.5*2^60 bis 216.5*2^60 liegt. Ansonsten müsste auch er vieeele weitere Mantissenstellen liefern.
Vor allem kann man eine solche Zahl nicht mehr mit 1 incrementieren. Das Ergebnis wird sich einfach nicht mehr erhöhen, weil 1 erst in der (Im Beispiel oben) >15ten Stelle ist und das Runden, bzw die Rechnung selbst schon diesen Wert wieder verwirft. Ein Zähler in Fliesskommazahl ist völliger Humbug wenn die Mantisse nicht mehr ausreicht. Und die ist immer kleiner als bei einem Ganzzahltyp der selben Bytegröße.
:
Bearbeitet durch User
Was gebe es denn auch zu zählen? Selbst wenn man den Zähler mit 10Ghz betreibt, braucht man 10Jahre um diese zahl zu erreichen.
was spricht gegen das Rechnen mit signed/unsigned integer? Da gitbs keine Unterteilung in Mantisse und Exponent., alle 32 (oder 31) Stellen stehen zur Verfügung. Ein Zähler schreit geradzu nach uint... Wenn 4294967296 nicht reicht, dann nimmt man halt 64 bit. Das geht immerhin bis 18446744073709551616. Zählst du mit 1MHz reicht das 7580853728921 Jahre.
Irgendwer23 schrieb: > was spricht gegen das Rechnen mit signed/unsigned integer? Zum einen daß er TE ja gar nicht selber zählt (hatte ich auch erst fälschlich angenommen) sondern einen "Zähler" von extern als Fließkommawert bekommt. Zum zweiten weil zumindest auf dem AVR die 64-bit Ganzzahlarithmetik ein paar ziemlich fette Runtime-Funktionen aus libgcc linkt. OK, einen ATmega2560 bringt man damit nicht ins Schwitzen. Aber auf einem ATtiny kann das schon ein bisschen zu viel sein. Andererseits gibt es einige Sammlungen von Ganzzahl-Arithmetikfunk- tionen, auch in diesem Forum (bzw. nebenan in der Codesammlung). Die sind dann in (Inline)Assembler und schneller und/oder kompakter und/oder flexibler als was mit avrgcc kommt. XL
kyrel schrieb: Vielleicht ist das nicht klar herausgekommen. Aber > Beispiel: > > Rechner: > Mantisse: 216, Exponent: 60, > ldexp(Mantisse, Exponent): 249031044995078946816 > > µC: > Mantisse: 216,Exponent: 60, > ldexp(Mantisse, Exponent) 249031050000000000000 > Wetten das das Zählergebnis eben nicht 249031044995078946816 war? Wie würde dein Zähler ein Ergebnis von 249031044995078946817 (also um 1 höher) übermitteln? Das Ergebnis vom µC ist genauso genau oder ungenau wie das vom PC. Denn das Problem ist nicht der µC bzw. der PC sondern das Problem ist, dass du in erter Linie mit Mantisse und Exponent da schon nichts besseres hinkriegst. Die nächst-'größere' Zahl, die du vom Zähler kriegen kannst, ist offensichtlich Mantisse: 217 Exponent: 60 Die ist aber von 216/60 schon so weit entfernt, dass es völlig sinnlos ist, da 23 Stellen in der Ausgabe anzuzeigen. Denn die hälfte davon ist sowieso gelogen bzw. hat nichts mit dem realen Zählergebnis bis auf die Einerstelle runter zu tun.
:
Bearbeitet durch User
Fließkommazahlen sind fehlerbehaftet, ähnlich JPG Bilder. Wenn man bei Fließkomma eine sehr kleine Zahl zu einer sehr großen Zahl addieren will, dann wird diese verschluckt, wenn die Anzahl signifikanter Stellen des Zahlenformats kleiner ist, als die Differenz der Potenzen der Zahlen. Beispiel mit 32 Bit Fließkomma.
1 | float a,b; |
2 | |
3 | a=1e10; |
4 | b=1e0; |
5 | |
6 | a+b==1e10 |
Und selbst wenn man 1e10 mal 1e0 addiert, die Summe bleibt 1e10! Ein Rundungsfehler, der sich akkumuliert!
>Die ist aber von 216/60 schon so weit entfernt, dass es völlig sinnlos >ist, da 23 Stellen in der Ausgabe anzuzeigen. Denn die hälfte davon ist >sowieso gelogen bzw. hat nichts mit dem realen Zählergebnis bis auf die >Einerstelle runter zu tun. Welcher IC zählt denn angeblich derartig viele Ereignisse? Da stimmt doch was nicht.
Falk Brunner schrieb: > Welcher IC zählt denn angeblich derartig viele Ereignisse? Da stimmt > doch was nicht. Das würde mich auch interessieren. Vor allen Dingen: was wird da eigentlich gezählt? Weiter oben hat das ja schon mal wer vorgerechnet. Selbst mit GHz Zählraten würde es Jahrmilliarden dauern um auf diese Zahlen zu kommen. Aber dazu schweigt der TO sich ja aus. Das ganze erinnert mich frappant an die immer gleichen "Ich will die Temperatur in meinem Wohnzimmer aufs tausendstel Grad genau messen" Fraktion, die einfach nicht verstehen will, dass eine derartige Angabe eben nicht 'möglichst genau' sondern "maximal gelogen und trotzdem wichtig aussehend" darstellt.
:
Bearbeitet durch User
Karl Heinz schrieb:
[sehr viel sinnvolles, vor allem die "next value"-Sache müßte jeden
tatsächlich denkenden Menschen überzeugen können]
Aber um was wollen wir wetten, daß das der TO nicht begriffen hat und
funktional verhindert ist, das auch zukünftig zu begreifen, selbst wenn
man es mit roher Gewalt in ihn reinprügeln würde?
kyrel schrieb: > ich arbeite hier mit einem Atmega2560 und Ganzzahlen, die, bedingt durch > ihre Größe, in Mantisse und Exponent geteilt sind. Jaja - und du hast so nebenbei erwähnt, daß du das Ganze in C erledigen willst. Sorry, aber das läuft auf Unsinn hinaus. Weder ist dein µC ein 64 Bit Rechner, noch ist m.W. bei der ATmega-Portierung ein ausreichendes Zahlenformat vorhanden. Für sowas mußt du dich eben mit Assembler befassen und für deine Probleme passende Funktionen schreiben. Nebenbei haben sowas schon ganz andere gemacht, Integerformate beliebiger Größe, damals für den Z80 - naja.. soweit der verfügbare Speicher und die benötigte Rechenzeit reicht. W.S.
W.S. schrieb: > Für sowas mußt du dich eben mit Assembler befassen und für deine > Probleme passende Funktionen schreiben. warum? Wenn es nicht sonderlich schnell gehen muss, kann man das auch in C machen.
Hallo nochmal, genau diese Denkanstöße wollte ich initiieren. Irgendwie hab ich die ganze Flieskommazahl-Darstellung doch wieder vergessen.... Danke an dieser Stelle uA an Axel Schwenke und Jim Meba. Ausschweigen....nunja,...eine hochgenaue Temperatur-Sensor-Anwendung ist das nicht! Aber genaueres darf ich nicht erzählen, insb. den IC nicht :( , eure Überlegungen wie 10GHz (wobei das noch moderat ist) ist nicht ganz verkehrt....sorry. Abgesehen von der Anwendung, ich wollte nur wissen, ob es - ob es sinnvoll ist oder nicht - wissen, ob es Möglichkeiten gibt die Rundungen nach hinten zu "versetzten". Dass es keine exakte, absolte (fehlerfreie) Zahl ist, ist auch klar, nur will ich den Rundungsfehler, aus Sicht des atmels eben kleinstmöglich halten. Danke an alle für eure Anregungen
Das wurde jetzt zwar schon zehnmal gesagt aber wenn du was zählen willst vergiss Fließkomma. Es kommt irgendwo ein Punkt (bei 8 bit Fließkomma sehr früh) da passiert einfach gar nix mehr wenn du a += 1 machst. Wenn du größere Zähler brauchst nimm mehrere ints und bau dir deine Addition selber. Wenn du nur Addition brauchst, ist das ja auch trivial zu machen.
Udo Schmitt schrieb: > Vor allem kann man eine solche Zahl nicht mehr mit 1 incrementieren. Das > Ergebnis wird sich einfach nicht mehr erhöhen, weil 1 erst in der (Im > Beispiel oben) >15ten Stelle ist und das Runden, bzw die Rechnung selbst > schon diesen Wert wieder verwirft. Noch viel schlimmer ist, dass man für den Exponent auch Bits verschenkt, die man viel besser für die Mantisse brauchen könnte. Manch mal sind ein paar Grundlagen gar nicht so schlecht, bevor man sich mit hochgeheimen IC beschäftigt.
Meckert nicht so mit "kyrel (Gast)". Der wurde vom geheimsten aller geheimen Geheimdienste als Praktikant mit einem Weltrettungs-Problem beauftragt! In Zukunft wird nämlich Mantisse und Exponent in geheimen Nachrichten vertauscht! Da werden sich NSA und außerirdische Invasoren die Zähne dran ausbeißen. Jawoll!!!
Also an der Datenquelle ("Zähler") herumzunörgeln hilft nichts. Wenn die Daten als Fließkomma herinkommen, kommen sie eben so, fertig. Wichtig ist doch, was man daraus macht. Der auffällige Unterschied zwischen µC und Rechner liegt daran, daß der avr-gcc eben nur mit float rechnet, selbst wenn man double deklariert. Relevant ist der Unterschied aber nur, wenn man die zusätzliche Genauigkeit auch braucht. Also: Wieviele Bits/Dezimalstellen hat denn die Mantisse, die da vom "Zähler" kommt? Wenn das weniger als 23bit bzw. 7 Dezimalstellen sind, reicht float zur Darstellung aus. Dann kann noch sein, daß für Berechnungen mehr Auflösung nötig ist. Da kann man vielleicht die Formeln geeignet umstellen, um das zu vermeiden. Und wenn das alles nichts hilft, ist eine Library für höhere Auflösung nötig, z.B. die von detlef_a oben.
@Oldi Mensch -Du bist ja der Komiker schlechthin! Kann man Dich auch buchen? Ich bräuchte noch jemanden mit Deinem Humor für eine Trauerfeier....
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.