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.
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.
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.
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_tzaehler[4]={0,0,0,0};
2
3
voidhochzaehlen(){
4
for(uint8_ti=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
voidhochzaehlen(){
2
for(uint8_ti=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
voidausgabe(){
13
for(uint8_ti=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.
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.
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
floata,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.
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.