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?
Nun mit 32 bit float rechnet man immer nur näherungsweise.. Und muss sich über den Zahlenbereich Gedanken machen.
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?
Bei der Konvertierung von float auf int entsteht ein Rundungsfehler. Aus 1.999 wird 1 Vielleicht hilft es vor dem casten noch 0.5 dazu zu addieren.
habe das ausprobiert: float a = 987.123; float b = 987.123; float c = a / b; printf("c = %f\n", c); es kommt 1 raus.
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.
:
Bearbeitet durch Moderator
gcc auf dem PC, der hat eine FPU falls das wichtig ist.
1 | int pwmBaseValue = 255,pwmIndex = 15; |
2 | int result = 0; |
3 | |
4 | result = pwmBaseValue * (pow(pwmIndex, EXPONENT)); |
5 | result /= pow(INDEX_MAX, EXPONENT); |
"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!
genauer gesagt kommt c = 1.000000 raus.
ne das steht in dem Betreff: Division eines Floatwertes mit sich selbst ergibt nicht 1
@Lothar & Karl: In dem Link unter "Result2", wie im Text oben schon beschrieben. Beides mal ist es ein int-Wert von "15"
Johannes H. schrieb: > Division eines Floatwertes mit sich selbst ergibt nicht 1. > Wisst ihr woran das liegen könnte? Du verwendest float :-)
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.html Johannes 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.
:
Bearbeitet durch Moderator
@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:
1 | ...
|
2 | const float EXPONENT = 1.8; |
3 | const int INDEX_MAX = 15; |
4 | int main(void) { |
5 | |
6 | int pwmBaseValue = 255,pwmIndex = 15; |
7 | int result = 0; |
8 | float resultf; |
9 | |
10 | resultf = pwmBaseValue * (pow(pwmIndex, EXPONENT));// / pow(INDEX_MAX, EXPONENT)); |
11 | result = resultf / pow(INDEX_MAX, EXPONENT); |
12 | printf("Result1 = %i",result); |
13 | ...
|
Dann wäre das Zwischenergebnis auch im ersten Fall ein nicht abgeschnittener Float.
:
Bearbeitet durch Moderator
@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.
:
Bearbeitet durch User
Johannes H. schrieb: > Wie wäre der Ansatz, das "pow"-Ergebnis als float oder int zu casten. Nein, nicht casten, sondern die richtige Umrechnung dazu benutzen. In dem Fall float/double to Integer sind das IMMER Round Funktionen. https://www.gnu.org/software/libc/manual/html_node/Rounding-Functions.html
>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.
:
Bearbeitet durch User
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 | float f = 1.99; |
2 | int i, 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
1 | j = (f >= 0) ? (f + 0.5) : (f - 0.5); |
:
Bearbeitet durch User
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.
Wolfgang schrieb: > Seit wann rundet man durch das Abschneiden der Nachkommastellen? Seit man den Thread gelesen und das Problem verstanden hat.
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.