Hallo Leute!
Ich suche gerade nach einer Möglichkeit die Anzahl der Nachkommastellen
einer float-Zahl mittels eines Arduino Micro zu bestimmen. Grundsätzlich
habe ich mir das so gedacht:
*Typecast der float-Zahl zu int
*Falls Differenz zwischen float-Zahl und int Zahl ungleich null, wird
die float-Zahl mit 10 multipliziert
*Zähler inkrementieren
*Schleife beginnt von vorne
Hier mal mein Code:
1
uint8_tgetDecimalPlace(floatval){
2
uint8_ti=0;
3
uint_32_tn=int(val);
4
while((val-n)!=0){
5
i++;
6
val*=10;
7
n=int(val);
8
}
9
returni;
10
}
Mit einer Nachkommastelle funktioniert das auch wunderbar. Wenn ich
jedoch zwei Nachkommastellen habe, gibt es anscheinend Probleme mit dem
Typecast. Zumindest spuckt mir mein Debugging für Arme (print() per
serieller Schnittstelle) etwas falsches aus:
Im ersten Schleifendurchlauf ist noch alles klar, val = 47.4, n = 47. Im
zweiten bekomme ich jedoch schon falsche Werte: val = 474, n= 473. Und
das pflanzt sich dann so fort und die Schleife wird nie verlassen.
Kann mir da jemand ein paar Tipps geben woran das liegen könnte oder ob
man das grundsätzlich anders machen sollte?
Grüße
Andi
Andi schrieb:> Kann mir da jemand ein paar Tipps geben woran das liegen könnte oder ob> man das grundsätzlich anders machen sollte?
Was du übersiehst ist, dass deine Zahl im Computer nicht dezimal
gespeichert wird sondern binär. Das bedeutet aber auch, dass sie anders
im Speicher steht und auch unter Umständen einen etwas anderen Wert hat.
Ohne jetzt die Binärrepräsentierung kontrolliert zu haben, ist es zb
möglich, dass die Zahl 47.4 in Binärform gar nicht exakt darstellbar
ist. Binär ist zum Beispiel 47.3999999 möglich. Das ist fast 47.4 aber
eben nicht ganz. Rundet man die Ausgabe auf 2 Stellen, dann steht in der
Ausgabe tatsächlich 47.4 (beim printf). Das bedeutet aber nicht, dass
die Zahl intern tatsächlich dem Wert 47.4 entspricht.
Floating Point rechnen ist in einem COmputer keineswegs eine simple
Sache und verhält sich nicht immer so, wie man sich das landläufig so
vorstellt. Zwischen 0.0 und 1.0 gibt es unendlich viele rationale
Zahlen. Dadurch dass man aber nicht unendlich viele Bits zur Verfügung
hat, muss man Abstriche machen. Nicht alle Zahlen zwischen 0.0 und 1.0
sind in einem Computer exakt darstellbar, sondern müssen durch die
nächstliegende Zahl (im Binärsystem) angenähert werden. Tatsächlich ist
sogar die große Mehrheit aller rationalen Zahlen nicht exakt
darstellbar.
Das ist ein bischen so, wie du das ausgerechnte Ergebnis von 1.0/3.0
(also 0.33333333...) eben nicht niederschreiben kannst, selbst wenn du
über alles Papier dieser Welt verfügen würdest. Und das Ergebnis davon,
das du praktisch erhältst ist dann eben nicht mehr 1.0, wenn man es mit
3.0 multipliziert. Das Ergebnis ist 0.99999999999... eine Zahl die fast
1.0 ist, aber eben nicht genau 1.0
So was macht man am besten gar nicht. zB die Zahl 0.1 ist nicht als
float darstellbar, sondern nur eine Approximation mit ca. 15
Dezimalziffern - willst du die wirklich im Ergebnis haben? (printf
rundet bei der Anzeige sodass es so aussieht als könne ein float 0.1
sein).
Wenn's doch sein muss zerleg die Zahl in mantisse & exponent, zähle die
nullen im ersteren und rechne damit ein bisschen rum.
PS: Genau nachlesen wie floating point funktioniert und überlegen ob
fixed point nicht doch besser ist. Arduino mit AVR ist eh extrem lahm
bei float.
Andi schrieb:> Kann mir da jemand ein paar Tipps geben woran das liegen könnte oder ob> man das grundsätzlich anders machen sollte?
Die Frage ist: was willst du eigentlich aus einer höheren Sicht aus
machen?
Der Normalfall ist, das man im Vorfeld weiß, wieviele Kommastellen
benötigt werden und nicht der, dass man die bestimmen muss.
Hanno schrieb:> Wandle die Zahl in einen String um.
Dann hat man aber entweder die Anzahl Ziffern einer gerundeten Zahl,
oder viel zu viele... Sehr nützlich.
Karl Heinz schrieb:> Ohne jetzt die Binärrepräsentierung kontrolliert zu haben, ist es zb> möglich, dass die Zahl 47.4 in Binärform gar nicht exakt darstellbar> ist.
Jop.
Dr. Sommer schrieb:> zB die Zahl 0.1 ist nicht als> float darstellbar, sondern nur eine Approximation
Jop.
Jede Zahl die hinter dem Komma auf 1,2,4,6 oder 8 endet ist binaer nicht
exat darstellbar, bzw. nur zahlen die hinter dem Komma auf 0 oder 5
enden sind binaer exakt darstellbar.
Beweis:
0.1 * 2 = 0.2
0.2 * 2 = 0.4 <--------------+
0.4 * 2 = 0.8 |
0.8 * 2 = 1.6 <-- uebertrag |
0.6 * 2 = 1.2 <-- uebertrag |
0.2 * 2 = 0.4 ---------------+
und so gehts weiter bis zum ende aller tage.
Andi schrieb:> Ich suche gerade nach einer Möglichkeit die Anzahl der Nachkommastellen> einer float-Zahl mittels eines Arduino Micro zu bestimmen.
Nimm dir als Startwert Delte z.B. die Zahl 0.5
Dann rechne
y = 1 - Delta
In einer Schleife halbierst du Delta immer weiter, bis y=1.0 raus kommt.
Wenn du dir dabei y immer ausgeben läßt, siehst du auch wieviel Stellen
deine Float-Zahlen repräsentieren können.
Karl Heinz schrieb:> Der Normalfall ist, das man im Vorfeld weiß, wieviele Kommastellen> benötigt werden und nicht der, dass man die bestimmen muss.
Ich habe hier acht Siebensegmentanzeigen, die ich ansteuern möchte und
würde gerne eine Funktion haben, in die ich die darzustellende Zahl
einfach eintippe, bspw.
1
showNumber(2432.97);
Mit ganzen Zahlen habe ich das bisher problemlos realisieren können, bei
Kommazahlen müsste ich jedoch wissen an welcher Stelle genau das Komma
steht, um die Anzeige dementsprechend anzusteuern.
Dr. Sommer schrieb:> Wenn's doch sein muss zerleg die Zahl in mantisse & exponent, zähle die> nullen im ersteren und rechne damit ein bisschen rum.
Daran habe ich auch bereits gedacht, nur wie komme ich an die Mantisse
und den Exponenten ran?
Vielen Dank! :)
Andi schrieb:> Karl Heinz schrieb:>> Der Normalfall ist, das man im Vorfeld weiß, wieviele Kommastellen>> benötigt werden und nicht der, dass man die bestimmen muss.>> Ich habe hier acht Siebensegmentanzeigen, die ich ansteuern möchte und> würde gerne eine Funktion haben, in die ich die darzustellende Zahl> einfach eintippe, bspw.
1
showNumber(2432.97);
> Mit ganzen Zahlen habe ich das bisher problemlos realisieren können, bei> Kommazahlen müsste ich jedoch wissen an welcher Stelle genau das Komma> steht, um die Anzeige dementsprechend anzusteuern.
Dann nützt Dir die Anzahl der Nachkommastellen sowieso nicht viel. Denn
wenn Du Dich allein danach richtest, weisst Du ja nicht ob die Zahl noch
in die Anzeige passt. Sie mag ja zuviele Vorkommastellen haben.
Runden aber geht immer. Und mit der Anzahl der Vorkommastellen, weisst
Du auch wo das Komma hingehört. Das geht mit dem Logarithmus zur Basis
10.
Wenn Karl Heinz was anderes schreibt hat im Zweifel er recht. :-)
Andi schrieb:> Daran habe ich auch bereits gedacht, nur wie komme ich an die Mantisse> und den Exponenten ran?
1
uint32_traw=*((uint32_t*)(&myFloat));
2
uint32_tmantissa=raw&0x7FFFFFFF;
3
uint32_texp=(raw>>23)&0xFF;
Es ist aber wie gesagt dringend davon abzuraten die Ziffern zählen zu
wollen... Denn zB wenn du die Anzahl Ziffern von 2432.97 abfragst, wirst
du 17 enthalten, denn 2432.97 ist nicht als float darstellbar und wird
stattdessen durch 2432.969970703125 approximiert, was 17 Stellen hat.
Wie willst du das jetzt darstellen??
Bitflüsterer schrieb:> Dann nützt Dir die Anzahl der Nachkommastellen sowieso nicht viel. Denn> wenn Du Dich allein danach richtest, weisst Du ja nicht ob die Zahl noch> in die Anzeige passt. Sie mag ja zuviele Vorkommastellen haben.
Ich richte mich nicht nur allein danach; mir ist schon klar, dass die
Zahl unter Umständen nicht voll anzeigbar ist. Aber das ist sowieso
nicht so wichtig, da der anzeigbare Zahlenbereich direkt durch die
Funktion eingeschränkt werden soll (einfachster Fall: Kommentar mit
"zahl=0...xxxxxxxx als Parameter möglich" vor der Funktion ;)).Das Ganze
ist eh nur für mich gedacht, d.h. das wird keine öffentliche Library, wo
im Zweifelsfall alle möglichen (und evtl. sinnlosen) Benutzereingaben
verarbeitet bzw. abgefangen werden sollen.
Dr. Sommer schrieb:> Es ist aber wie gesagt dringend davon abzuraten die Ziffern zählen zu> wollen... Denn zB wenn du die Anzahl Ziffern von 2432.97 abfragst, wirst> du 17 enthalten, denn 2432.97 ist nicht als float darstellbar und wird> stattdessen durch 2432.969970703125 approximiert, was 17 Stellen hat.> Wie willst du das jetzt darstellen??
Das habe ich tatsächlich nicht bedacht, danke für den Einwand! ;)
Andi schrieb:> Bitflüsterer schrieb:>> Dann nützt Dir die Anzahl der Nachkommastellen sowieso nicht viel. Denn>> wenn Du Dich allein danach richtest, weisst Du ja nicht ob die Zahl noch>> in die Anzeige passt. Sie mag ja zuviele Vorkommastellen haben.>> Ich richte mich nicht nur allein danach; mir ist schon klar, dass die> Zahl unter Umständen nicht voll anzeigbar ist. Aber das ist sowieso> nicht so wichtig, da der anzeigbare Zahlenbereich direkt durch die> Funktion eingeschränkt werden soll (einfachster Fall: Kommentar mit> "zahl=0...xxxxxxxx als Parameter möglich" vor der Funktion ;)).Das Ganze> ist eh nur für mich gedacht, d.h. das wird keine öffentliche Library, wo> im Zweifelsfall alle möglichen (und evtl. sinnlosen) Benutzereingaben> verarbeitet bzw. abgefangen werden sollen.
Ob das nun eine "öffentlich" Library werden soll oder nicht spielt dabei
garkeine Rolle.
Normalerweise hat man eine Forderung, das Zahlen in dem und den Bereich
mit den und den Nachkommastellen angezeigt werden sollen.
Ob Du Die nun per Kommentar oder sogar funktional einschränkst ist eine
Sache. Die andere aber ist, wie Du konkret die Zahl anzeigst. Welche
Methoden usw. Du anwendest.
Nur darauf bezog sich meine Antwort.
Und eben dabei die Anzahl der der Nachkommastellen heranzuziehen ob
allein oder mit Einschränkung eines Wertebereichs ist ein komplizierter
Umweg im Vergleich zu der Methode die Anzahl der Vorkommastellen
festzustellen. Meine ich wenigstens.
Aber gut. Vielleicht verstehe ich Deine Frage nicht und halte mich
besser 'raus.