Hallo,
Im Debugsimulator im AtmelStudio habe ich festgestellt, dass bei einer
Berechnung anstatt 255 leider nur 254 rauskommt.
Unter diesem Link (https://ideone.com/iXZIJD) habe ich die Funktion mal
ausprobiert, da funktioniert es ohne Probleme (Result2), aber nicht bei
mir im ATTiny85.
Wisst ihr woran das liegen könnte?
Ich hatte mir im Debugsimulator mal die Nachkommastellen anschauen
wollen, aber da steht dann im Watchfenster immer "optimized away" wenn
ich "result" als float definiere. Als int wird es aber angezeigt. Was
mach ich da noch falsch?
float macht doch keine genaue Berechnung oder je nach Wert sind die
Schritte die dargestellt werden können ja unterschiedlich gross. Hängt
vielleicht einfach mit deiner float berechnungs Software zusammen oder
hast du eine FPU?
Wauwau schrieb:> habe das ausprobiert:
Womit?
Johannes H. schrieb:> Wisst ihr woran das liegen könnte?
Daran, dass das eben nicht die gleichen Zahlen sind. Zeig doch mal
deinen Code mit der Division und wie du da auf die gleichen Werte
kommst.
"result" ist vom Typ Integer, damit wird das Ergebnis der Multiplikation
implizit in "int" gecasted, so dass dort der Wert 33381 steht. Das
Gleiche nach der anschließenden Division, zunächst ergibt sich hier
(33381 / 130.9 = 254.9). Dieses Ergebnis wird aber wiederum implizit in
Integer gecasted, daher ergibt sich 254.
Hallo Karl,
Ja, aber selbst wenn nur näherungsweise gerechnet wird, sollte doch
"Wert/Wert = 1" sein, oder?
@Hans Ich hab den ATTiny85, der hat keine FPU.
Warum funktioniert es denn aber in dem angehängten Link?
Wauwau schrieb:> habe das ausprobiert:> float a = 987.123;> float b = 987.123;> float c = a / b;> printf("c = %f\n", c);>> es kommt 1 raus.
Nöö, das ist nicht sein Beispiel:
1
result=pwmBaseValue*(pow(pwmIndex,EXPONENT));
Es wird mit int16_t gerechnet und auch abgeschnitten!
Johannes H. schrieb:> Ja, aber selbst wenn nur näherungsweise gerechnet wird, sollte doch> "Wert/Wert = 1" sein, oder?
In der geposteten Formel ist nirgends Wert/Wert zu sehen.
Johannes H. schrieb:> Warum funktioniert es denn aber in dem angehängten Link?
Weil dort zur Vereinfachung gleich mit 64 Bit breiten Floats gerechnet
wird. Hier mal ein paar Worte zur Gleitkommadarstellung:
http://www2.informatik.uni-halle.de/lehre/c/c623.htmlJohannes H. schrieb:> Beides mal ist es ein int-Wert von "15"
Aber die Berechnung, die.mit dieser 15 durchgeführt wird, wird auf 2
verschiedenen Plattformen durchgeführt. Die Konstante wird auf dem PC
berechnet, die Variable auf dem uC. Und natürlich kommt da bei der
gleichen Rechnung was anderes raus. Siehe den Link vom letzten
Abschnitt.
Fazit: wenn man etwas genau haben möchte, dann nimmt man Integer.
@Lothar
Na das war doch sinnbildlich gemeint. Ich hätte auch schreiben können:
4/4 = 1
> wird auf 2 verschiedenen Plattformen durchgeführt.
Das hast Du misverstanden. Das Programm habe ich, als es auf dem µC
nicht funktionierte, im Debugsimulator laufen lassen um zu schauen
woran es liegt.
Wie soll ich denn integer verwenden, wenn die pow-Funktion einen
Fließkommawert zurückgibt?
Johannes H. schrieb:> Wie soll ich denn integer verwenden, wenn die pow-Funktion einen> Fließkommawert zurückgibt?
Das braucht ein wenig Nachdenken. Und evtl eine pow-Funktion, die auch
mit Integern kann...
Seis drum.
Die beiden Berechnungen in dem verlinkten ideone-Codeschnipsel sind
natürlich nicht gleich, weil bei der ersten Rechnung ein Zwischenschritt
über einen Integer gemacht und dabei alle Nachkommastellen abgeschnitten
werden.
Probiers doch mal so:
@Lothar
Ja aber da lande ich ja wieder bei meiner ursprünglichen Formel. Diesen
Zwischenschritt hatte ich nur eingefügt, weil ich die Berechnung
analysieren wollte.
Wie wäre der Ansatz, das "pow"-Ergebnis als float oder int zu casten? Da
wird doch theoretisch ein Stück des Nachkommawertes abgeschnitten bzw.
die Nachkommastellen ganz entfernt. Und dann müsste meine Berechnung ja
funktionieren.
>Wie wäre der Ansatz, das "pow"-Ergebnis als float oder int zu casten? Da
Also eine nachträgliche Manipulation der Zahlen - so lange bis das
gewünschte Ergebnis herauskommt - ist bestimmt das Gelbe vom Ei;-)
@Augen auf
Ok, daran hatte ich gar nicht gedacht :)
@Amateur
Ja, denn es ist nur wichtig, dass bei der Division der beiden
pow-Funktionen (wenn sie die selben Werte bekommen) eine glatte 1
rauskommt, bzw. 255 im Endresultat.
Johannes H. schrieb:> Ja, denn es ist nur wichtig, dass bei der Division der beiden> pow-Funktionen (wenn sie die selben Werte bekommen) eine glatte 1> rauskommt, bzw. 255 im Endresultat.
Warum?
Denn wenn es nur ein klitzekleiner "Mückenschiss" weniger ist, dann
kommt auch nicht mehr 255 heraus. Du betrachtest also eigentlich einen
ausserordentlich seltenen Fall.
Und wenn die 255 kein sehr seltener Fall sein soll, dann sollte die
Berechnung des Wertes 255 nicht so dermaßen knapp auf der Kippe stehen.
Dann könntest du vor dem Abschneiden der Nachkommastellen auch einfach
aufrunden
result = resultf / pow(INDEX_MAX, EXPONENT) + 0.5;
Siehe https://ideone.com/i0cqkt
Johannes H. schrieb:>> wird auf 2 verschiedenen Plattformen durchgeführt.>> Das hast Du misverstanden.
Nein. Du hast das immer noch nicht verstanden.
> Das Programm habe ich, als es auf dem µC> nicht funktionierte, im Debugsimulator laufen lassen um zu schauen> woran es liegt.
Das ist gar nicht der Punkt. Der erste Aufruf von pow() wird auf dem µC
zur Laufzeit berechnet. Der zweite Aufruf von pow() ist aber konstant
und deswegen berechnet den der Compiler, wenn er dein Programm
compiliert. Im µC-Programm steht an dieser Stelle eine vorberechnete
float Konstante.
Dein grundsätzlicher Fehler besteht aber darin, daß du float Zahlen
direkt nach int castest. Das ist immer falsch. Der korrekte Weg
besteht darin, mit round() zu konvertieren. Oder wenn du schon weißt,
das die float Zahl "eigentlich" ganzzahlig sein sollte, dann kannst du
auch 0.5 addieren und dann nach int casten.
Ok, das mit der berechneten Konstanten hab ich jetzt verstanden.
Die Variante mit "+ 0.5" funktioniert gut. Da spart man wahrscheinlich
auch Rechenzeit und Programmspeicher als wenn man den Wert runden würde,
oder?
Hi
Durch das +0.5 gibst Du quasi diese Rundung vor.
Alles, was vorher x.5 (oder größer) war, ist danach (x+1).0-4
Da die Nachkommastelle bei INT (bzw. beim 'cast' zu int) abgeschnitten
wird, ist so aufgerundet.
Bis zur x.4 bekommst Du nur x.9 raus, abgeschnitten verbleibt x und
damit abgerundet.
Könnte mir vorstellen, daß Das etwas Rechenzeit einspart.
MfG
Joe F. schrieb:> Bei der Konvertierung von float auf int entsteht ein Rundungsfehler.> Aus 1.999 wird 1
Seit wann rundet man durch das Abschneiden der Nachkommastellen?
Wolfgang schrieb:> Joe F. schrieb:>> Bei der Konvertierung von float auf int entsteht ein Rundungsfehler.>> Aus 1.999 wird 1>> Seit wann rundet man durch das Abschneiden der Nachkommastellen?
Es wird ja beim Casten von float auf int eben nicht gerundet, sondern
abgeschnitten.
1
floatf=1.99;
2
inti,j;
3
i=f;
4
j=(f+0.5);
5
printf("i=%d j=%d\n",i,j);
ergibt:
1
i=1 j=2
daher sagte ich ja: 0.5 dazu addieren. Geht natürlich nur bei positiven
Zahlen.
Universell wäre
Axel S. schrieb:> Johannes H. schrieb:>>>> wird auf 2 verschiedenen Plattformen durchgeführt.>>>> Das hast Du misverstanden.>> Nein. Du hast das immer noch nicht verstanden.>>> Das Programm habe ich, als es auf dem µC>> nicht funktionierte, im Debugsimulator laufen lassen um zu schauen>> woran es liegt.>> Das ist gar nicht der Punkt. Der erste Aufruf von pow() wird auf dem µC> zur Laufzeit berechnet. Der zweite Aufruf von pow() ist aber konstant> und deswegen berechnet den der Compiler, wenn er dein Programm> compiliert. Im µC-Programm steht an dieser Stelle eine vorberechnete> float Konstante.
Die auf dem Host berechnete Konstante wird aber nicht nach den Regeln
des Host-Rechners berechnet, sondern gemäß dem float-Format des Targets.
GCC verwendet dazu u.a. MPFR.
Das grundlegende Missverständnis ist, dass bei vorgegebenen float-Werten
und vorgegebener Plattform immer ein und dasselbe Ergebnis herauskommen
müsse, was schlichweg nicht der Fall ist. Das Ergebnis muss lediglich
der Spezifikation (z.B. IEEE) entsprechen.