Division eines Floatwertes mit sich selbst ergibt nicht 1

OP #4884197
Lesenswert?

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?
Gast #4884208
Lesenswert?

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?
Gast #4884222
Lesenswert?

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.
Gast #4884225
Lesenswert?

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!
Moderator (Firma: Titel) Persönliche Seite #4884243
Lesenswert?

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.
OP #4884257
Lesenswert?

@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?
Moderator (Firma: Titel) Persönliche Seite #4884270
Lesenswert?

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.
OP #4884558
Lesenswert?

@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.
Gast #4884577
Lesenswert?

>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;-)
Moderator (Firma: Titel) Persönliche Seite #4884600
Lesenswert?

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
#4884623
Lesenswert?

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.
#4886639
Lesenswert?

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
#4886728
Lesenswert?

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);
Persönliche Seite #4886731
Lesenswert?

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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren