Forum: Mikrocontroller und Digitale Elektronik Einfacher Fehler in C-Berechnung


von Werz (Gast)


Lesenswert?

Hallo,

ich habe hier einen Fehler in der Berechnung, der vermutlich von 
Rundungen / falschen Casts etc herrührt. Wenn ich es im Taschenrechner 
berechne, kommt das richtige raus, allerdings nicht i Code. Das ganze 
rennt auf einem AT32UC3C1128C.

hier dir Formel:
1
LiquLev = 10 * (float)((-0,000004 * (LiftParamR1Vol/10) * (LiftParamR1Vol/10) + 0,0405 * LiftParamR1Vol - 0,003));

LiquLev und LiftParamR1Vol ist vom Typ uint16

LiftParamR1Vol hat zum Berechnugszeitpunkt den Wert 1300, habe das auch 
im Debug gesehen -> in dem fall sollte für LiqLev ca 51 oder 52 
rauskommen, es kommt aber 30 raus.

Wo ist hier der fehler?

Danke!

:
von Hi (Gast)


Lesenswert?

"." statt "," wäre schon mal ein guter Anfang ;)

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Das ist nicht dein echter Quelltext. Zeig deinen echten Quelltext, sonst 
wird das so eine typische scheibchenweis Salami.

Beitrag #6811853 wurde von einem Moderator gelöscht.
von Philipp Klaus K. (pkk)


Lesenswert?

Werz schrieb:
> LiquLev = 10 * (float)((-0,000004 * (LiftParamR1Vol/10) *
> (LiftParamR1Vol/10) + 0,0405 * LiftParamR1Vol - 0,003));

Warum sollte es das nicht sein? Das ist gültiger C code, der, wenn man 
die beiden Deklarationen hinzufügt, und es in eine Funktion packt, 
sowohl mit SDCC als auch mit GCC ohne Warnungen kompiliert:

~~~~
#include <stdint.h>

uint16_t LiquLev, LiftParamR1Vol;

void f(void)
{
  LiquLev = 10 * (float)((-0,000004 * (LiftParamR1Vol/10) * 
(LiftParamR1Vol/10) + 0,0405 * LiftParamR1Vol - 0,003));
}
~~~~

Allerdings vermute auch ich, dass die dreifache Verwendung des 
Kommaoperators hier vom Programmierer nicht beabsichtigt war.

P.S.: In der Vorschau funktionieren die Tilden, um Code zu markieren, 
aber im Post selbst anscheinend nicht.

: Bearbeitet durch User
von Daniel B. (dbuergin)


Lesenswert?

wie schon oben geschrieben kommt 30 heraus, wenn man "," als 
Dezimalpunkt verwendet, was wohl nicht gewollt ist.
Nimmt man "." kommt 525 heraus:
1
#include <stdio.h>
2
#include <stdint.h>
3
4
void main(void)
5
{
6
        uint16_t LiquLev;
7
        uint16_t LiftParamR1Vol = 1300;
8
9
        LiquLev = 10 * (float)(-0.000004 * (LiftParamR1Vol/10) * (LiftParamR1Vol/10) + 0.0405 * LiftParamR1Vol - 0.003);
10
11
        printf("LiquLdev: %d\n", LiquLev);
12
}
13
14
gcc -o xx xx.c
15
./xx
16
LiqLev: 525

Alles auf einem Linux Server....

: Bearbeitet durch User
von Joe G. (feinmechaniker) Benutzerseite


Lesenswert?

Virtual C auf Windows

mit "." statt ","

LiftParamR1Vol = 130
LiquLev 52

LiftParamR1Vol = 1300
LiquLev 525

von Christian H. (netzwanze) Benutzerseite


Lesenswert?

Joe G. schrieb:
> LiftParamR1Vol = 1300
> LiquLev 525

Das wäre nur eine Zehnerpotenz daneben. Hat damit eventuell die "10 *" 
zu tun?

von Philipp Klaus K. (pkk)


Lesenswert?

Ist wirklich beabsichtigt, die Division durch 10 als Ganzzahldivision 
durchzuführen? Beim Testwert (130 oder 1300, also einem Vielfachen von 
10) ergibt es zwar keinen Unterschied, aber wenn man eine 
Gleitkommadivision eines float will (man also bei 13/10 lieber 1.3f 
statt 1 erhält) sollte man die 10 durch 10.0f ersetzen.

Auch ansonsten ist mir nicht klar, welche Genauigkeit gewünscht ist. Mit 
Zuweisung an float am Ende, aber 0.0405 in der Berechnung würde mit 
double gerechnet, und am Ende auf float gerundet. das könnte erwünscht 
sein, muss es aber nicht. Wenn es schnell gehen soll und die Genauigkeit 
von float ausreicht, kann man alles als float rechnen (also mit 0.0405f 
statt 0.0405).

von Gerald B. (geraldb)


Lesenswert?

Vermutlich ist seine Formel auch falsch. Da fehlt sicherlich noch eine 
Division durch 10. Ich tippe mal, es müßte heißen:
1
LiquLev = 10 * (float)(-0.000004 * (LiftParamR1Vol/10) * (LiftParamR1Vol/10) + 0.0405 * LiftParamR1Vol/10 - 0.003);

von Der Opa aus der Muppet Show (Gast)


Lesenswert?

Schon 6 Fehler in einer Programmzeile gefunden?

Ey Leute, die Frage ist doch nicht ernst gemeint. So viele Fehler in 
einer Zeile - das kann kein Zufall sein. Der gute Mann hat doch 
absichtlich die maximale Anzahl an Fehlern eingebaut.

von das schulsystem ist Schuld (Gast)


Lesenswert?

Der Opa aus der Muppet Show schrieb:
> Schon 6 Fehler in einer Programmzeile gefunden?
>
> Ey Leute, die Frage ist doch nicht ernst gemeint. So viele Fehler in
> einer Zeile - das kann kein Zufall sein.

10.0 statt 10 wäre auch eine gute Idee.
Dies Anhäufung ist sicher kein Zufall sondern Ausdruck eines 
pädagogischen Totalversagens. Das passiert eben, wenn man Schultrottel 
an einen Compiler lässt, ohne vorher eine Schulung/Prüfung zu 
durchlaufen.

von Gerald B. (geraldb)


Lesenswert?

Oder evtl. auch noch:
1
LiquLev = 10 * (float)(-0.000004 * (LiftParamR1Vol/10) * (LiftParamR1Vol/10) + 0.0405 * LiftParamR1Vol/10 - 0.003/10);

von Lutz (Gast)


Lesenswert?

Sehe ich auch so: Troll!

von Wolfgang (Gast)


Lesenswert?

Werz schrieb:
> Wo ist hier der fehler?

Finde es raus. Guck dir die Werte der Teilterme an und vergleiche sie 
mit dem Wert, den du jeweils erwartest. Da wo die Abweichung auftritt, 
ist höchstwahrscheinlich der Fehler nicht weit.

von das schulsystem ist Schuld (Gast)


Lesenswert?

Werz schrieb:
> LiquLev und LiftParamR1Vol ist vom Typ uint16

schon mal überlegt wofür das 'u' in uint steht?

von das schulsystem ist Schuld (Gast)


Lesenswert?

Und 'Stellenauslöschung' kennt man auch nur vom 
Gewerkschaftsstammtisch?!

https://de.wikipedia.org/wiki/Ausl%C3%B6schung_(numerische_Mathematik)

von das schulsystem ist Schuld (Gast)


Lesenswert?

Werz schrieb:
> AT32UC3C1128C

Und um einen 32bit Prozessor mit 16bit Daten zu quälen braucht man auch 
gute Gründe ...

Vielleicht hätte man doch erst mit einer Sprache ohne 
Type-konvetierungen mit eingebauten "Schuß ins Knie" anfangen sollen. 
Und sei es um nur den Algo vor der C-Implementierung zu verifizieren.

von Rolf M. (rmagnus)


Lesenswert?

Wolfgang schrieb:
> Werz schrieb:
>> Wo ist hier der fehler?
>
> Finde es raus. Guck dir die Werte der Teilterme an und vergleiche sie
> mit dem Wert, den du jeweils erwartest. Da wo die Abweichung auftritt,
> ist höchstwahrscheinlich der Fehler nicht weit.

Genau. Prinzip "teile und herrsche". Also statt einen Monsterterm 
hinzuschreiben und dann damit überfordert zu sein, dass das Endergebnis 
nicht stimmt, einfach in einzelne Teile zerlegen und die separat 
berechnen und ausgeben lassen.

von PittyJ (Gast)


Lesenswert?

Früher gab es noch printf.
Dann hat man die Formel in einzelne Teile zerlegt, und sich jeden 
Schritt ausgeben. Dann hat man die Schritte mit seiner 'Papier'-Rechnung 
verglichen.

Ich kann nicht verstehen, warum man das printf abgeschafft hat. Es hat 
früher so vielen Programmierern geholfen. Die heutige Jugend kann jetzt 
nur noch raten, wo die Fehler sind.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

PittyJ schrieb:
> Ich kann nicht verstehen, warum man das printf abgeschafft hat.

Wohin "printet" dein printf auf einem UC3?

von Guest (Gast)


Lesenswert?

-0,000004 ist vermutlich schon zu klein für float.

Formel so umsortieren, damit das erst am Ende kommt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Guest schrieb:
> -0,000004 ist vermutlich schon zu klein für float.

[ ] Du hast das Prinzip von Gleitkommazahlen verstanden

von Guest (Gast)


Lesenswert?

edit: nicht zu klein, aber der wertebereich von float ist recht klein 
... Da wird dann nichts mehr sinnvolles rauskommen, wenn man das als 
ersten term benutzt

von Guest (Gast)


Lesenswert?

Jörg W. schrieb:
> Guest schrieb:
>> -0,000004 ist vermutlich schon zu klein für float.
>
> [ ] Du hast das Prinzip von Gleitkommazahlen verstanden

Jau, IEE754 implementiert.

Ich weiß, was float kann und das ist schon grenzwertig.

von Roland F. (rhf)


Lesenswert?

Hallo,
Jörg W. schrieb:
> Wohin "printet" dein printf auf einem UC3?

Nun ja, man könnte sprintf() nutzen und die erhaltene Zeichenkette per 
serieller Schnittstelle ausgeben.

Oder man prüft die Berechnung in diesem Fall mit einem kurzen Programm 
auf einem PC.

rhf

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Guest schrieb:
> Ich weiß, was float kann und das ist schon grenzwertig.

Ein Problem wäre es, wenn in einem Teil eines additiven Ausdrucks eine 
sehr große und in einem anderen eine sehr kleine Zahl steht.
1
130 · 130 · -0.000004 = -0.0676
2
1300 · 0.0405 = 52.65
3
-0.03 / 10 = -0.003

Wenn hier was under den Tisch fällt, dann ist es der konstante Summand 
in der dritten Zeile. Vomm quadratischen Summanden der ersten Zeile sind 
zumindest noch zwei Stellen signifikant für float – allerdings hat der 
lineare Summand der zweiten Zeile mit Abstand den größten Einfluss aufs 
Ergebnis.

[Doofen Rechenfehler korrigiert]

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Roland F. schrieb:
> Nun ja, man könnte sprintf() nutzen und die erhaltene Zeichenkette per
> serieller Schnittstelle ausgeben.

Irgendwie scheint er es ja überprüft zu haben, vielleicht auch mit'm 
Debugger.

von ALLES nicht so einfach (Gast)


Lesenswert?


von Joe G. (feinmechaniker) Benutzerseite


Lesenswert?

Man kann ja eine quadratische Funktion auch ordentlich faktorisieren.

~~~~
#include <stdio.h>
#include <stdint.h>

int main(void)
{
  uint16_t LiquLev;
  uint16_t LiftParamR1Vol = 1300;

  LiquLev = (float)-4E-7*(((LiftParamR1Vol) * (LiftParamR1Vol) - 
1012500.0 * LiftParamR1Vol + 75000.0));

  printf("LiquLdev: %d\n", LiquLev);
}
~~~~

von PittyJ (Gast)


Lesenswert?

Jörg W. schrieb:
> PittyJ schrieb:
>> Ich kann nicht verstehen, warum man das printf abgeschafft hat.
>
> Wohin "printet" dein printf auf einem UC3?

Ich habe hier einem STM32H
Da mache ich snprintf() in einen char Buffer.
Danach gibt HAL_UART_Transmit() mir das auf eine serielle Schnittstelle 
aus.
Irgendeine Kommunikation muss doch vorhanden sein???

Also den Leuten heute muss man wirklich alles vorkauen...

von Udo S. (urschmitt)


Lesenswert?

Habt ihr jetzt seine Hausaufgaben gemacht?

SCNR

von W.S. (Gast)


Lesenswert?

Jörg W. schrieb:
> Guest schrieb:
>> -0,000004 ist vermutlich schon zu klein für float.
>
> [ ] Du hast das Prinzip von Gleitkommazahlen verstanden

Hihi.. Naja, ein unbeabsichtigtes Stückchen Wahrheit ist schon dabei: es 
ist aus meiner Sicht sinnlos, so eine Formel im Quellcode 
hinzuschreiben, sowas sollte man sinnvollerweise zusammenkürzen, so daß 
sie kein fast endloser Bandwurm mehr ist. Ebenfalls halte ich es für 
sinnvoll, das einzige echte Argument (LiftParamR1Vol) zuvor im float 
Format zu haben und auf eigentlich überflüssige typcasts zu verzichten. 
Aber es scheint mir, als ob gerade in Kreisen der C-Programierer lieber 
händeweise typcasts in den Quellcode geworfen würden, als zuvor mal 
nachzudenken.

W.S.

von floater (Gast)


Lesenswert?

Werz schrieb:
> LiquLev = 10 * (float)((-0,000004 * (LiftParamR1Vol/10) *
> (LiftParamR1Vol/10) + 0,0405 * LiftParamR1Vol - 0,003));

Grundregel: Wenn kein Exponent angegeben wird, dann MUSST Du den 
Dezimalpunkt angeben... zb LiftParamR1Vol/10.0

von Andras H. (kyrk)


Lesenswert?

Werz schrieb:
> LiquLev = 10 * (float)((-0,000004 * (LiftParamR1Vol/10) *
> (LiftParamR1Vol/10) + 0,0405 * LiftParamR1Vol - 0,003));

Fehler ist : )) Anfang hast du 2 und am ende auch 2. Wieso? Entwäder zu 
viele, oder eins ist an einer falsche stellt.

Da ich bei C solche Sachen mir nie merken kann wie und wann eine 
Variable konvertiert oder kastiert wird, mache ich lieber immer so:

float LiftParamR1Vol_f = (float) LiftParamR1Vol;
float LiftParamR1Vol_div10_f = (float) LiftParamR1Vol_f/10.0;
float A = -0.000004  LiftParamR1Vol_div10_f  LiftParamR1Vol_div10_f;
float B = 0.0405 * LiftParamR1Vol_f;
LiquLev = A;
LiquLev += B;
LiquLev -= 0.003  <-- Das hier macht nicht viel Sinn. Da muss bestimmt 
eine () falsch irgendwo sein
LiquLev *= 10.0;

von Wolfgang (Gast)


Lesenswert?

Guest schrieb:
> -0,000004 ist vermutlich schon zu klein für float.

-0,000004 ist nicht einmal eine float-Konstante

von Erich (Gast)


Lesenswert?

42

Beitrag #6812528 wurde von einem Moderator gelöscht.
Beitrag #6812535 wurde von einem Moderator gelöscht.
Beitrag #6812568 wurde von einem Moderator gelöscht.
Beitrag #6812588 wurde von einem Moderator gelöscht.
Beitrag #6812594 wurde von einem Moderator gelöscht.
von Rainer V. (a_zip)


Lesenswert?

Richtig verwundert es mich, dass c-hater nicht da ist...
Gruß Rainer

von Fpgakuechle K. (Gast)


Lesenswert?

Rainer V. schrieb:
> Richtig verwundert es mich, dass c-hater nicht da ist...
> Gruß Rainer

wobei das entgegen der Thread-Headline kein orginäres C-Problem ist, 
sondern ein grundsätzliches Verständnissproblem im numerischen Rechnen 
des TO's offenbart.

Siehe auch: 
https://www-num.math.uni-wuppertal.de/fileadmin/mathe/www-num/teaching/ode_1415/Skript_Numerik1.pdf 
Kap.2

Das Gänze wird auch als 'Konditionierung' bezeichnet. Gut konditioniert 
ist ein Algorithmus, wenn sich kleine Veränderungen (Störungen) der 
Eingangsgrößen nur gering auf das rgebnis auswirken.

Siehe auch https://en.wikipedia.org/wiki/Condition_number

von Fpgakuechle K. (Gast)


Lesenswert?

Und Division sollte man auch aus Rechenzeitgründen vermeiden resp. in 
Multiplikation mit Reziprok umwandeln.
also statt
1
(-0,000004 * (LiftParamR1Vol/10) * (LiftParamR1Vol/10)

simpler
1
-0.4E-7 * LiftParamR1Vol* LiftParamR1Vol

von A. S. (Gast)


Lesenswert?

Fpgakuechle K. schrieb:
> Und Division sollte man auch aus Rechenzeitgründen vermeiden resp. in
> Multiplikation mit Reziprok umwandeln.

Die Formel macht insgesamt keinen Sinn. Solange der TO nicht sagt, wo er 
das her hat, oder was das soll, ist es vergebliche Mühe.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A. S. schrieb:
> Die Formel macht insgesamt keinen Sinn.

Naja, ist 'ne quadratische Approximation von irgendwas. Koeffizienten 
möglicherweise empirisch ermittelt, wobei nicht ganz klar ist, was die 
Dividiererei und Multiplizierere mit den Zehnen soll.

von Rainer V. (a_zip)


Lesenswert?

A. S. schrieb:
> Solange der TO nicht sagt, wo er
> das her hat, oder was das soll, ist es vergebliche Mühe.

war es und is es...

von Stefan F. (Gast)


Lesenswert?

Fpgakuechle K. schrieb:
> also statt:   (-0,000004 · (LiftParamR1Vol/10) · (LiftParamR1Vol/10)
> simpler:      -0.4E-7 · LiftParamR1Vol · LiftParamR1Vol

Erledigt nicht der Compiler derartige Optimierungen von selbst?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan ⛄ F. schrieb:
> Erledigt nicht der Compiler derartige Optimierungen von selbst?

Normalerweise schon, aber in dem Falle hat er nur beim AVR die Division 
komplett eliminiert (dort wäre sie aber auch am teuersten), während er 
es bei ARM und amd64 nicht getan hat.

von A. S. (Gast)


Lesenswert?

Jörg W. schrieb:
> Normalerweise schon, aber in dem Falle hat er nur beim AVR die Division
> komplett eliminiert (dort wäre sie aber auch am teuersten), während er
> es bei ARM und amd64 nicht getan hat.

Das wundert mich: Der Compiler eliminiert ein (uint16/10)? Das heisst, 
z.B. 0.4*(9/10) werden dann nicht 0, sondern 0.36?

von Stefan F. (Gast)


Lesenswert?

A. S. schrieb:
> 0.4*(9/10)

Wird vom Compiler komplett weg optimiert und durch eine Konstante 
ersetzte. Das macht er mit allen konstanten Ausdrücken und 
Teilausdrücken.
1
#include <stdio.h>
2
int main() 
3
{
4
    printf("%f",0.4*(9/10));
5
}
Erzeugt auf meinem PC die Ausgabe "0.000000".

Begründung: Der Teilausdruck 9/10 ergibt 0. Da beide Operanden Integer 
sind, wird die Berechnung mit einem Algorithmus durchgeführt, der 
Integer zurück liefert, also 0.

Anders sieht es aus, wenn man die Klammer weg lässt. Dann löst er 
nämlich von links nach rechts auf: 0.4·9 ergibt 3.6 und der nächste 
Schritt 3.6/10 ergibt dann 0.36.

Im obigen Programm sind aber Variablen beteiligt, deswegen muss der 
Ausdruck zur Laufzeit berechnet werden. Meine Frage zielte darauf ab, 
dass der Compiler den Ausdruck automatisch umstellt, wenn das für die 
Performance gut ist.

Ich weiß zum Beispiel mit Sicherheit, dass x/4 automatisch auf eine 
Shift Operation umgebaut wird, wenn x eine Integer Variable ist.

von Rolf M. (rmagnus)


Lesenswert?

Stefan ⛄ F. schrieb:
> A. S. schrieb:
>> 0.4*(9/10)
>
> Wird vom Compiler komplett weg optimiert und durch eine Konstante
> ersetzte.

Darum geht es doch hier gar nicht.

> Im obigen Programm sind aber Variablen beteiligt, deswegen muss der
> Ausdruck zur Laufzeit berechnet werden. Meine Frage zielte darauf ab,
> dass der Compiler den Ausdruck automatisch umstellt, wenn das für die
> Performance gut ist.

Aber auch zur Laufzeit darf er das nicht tun, wenn sich das Ergebnis 
dadurch ändern würde. Es ging ja um das hier:

Stefan ⛄ F. schrieb:
> Fpgakuechle K. schrieb:
>> also statt:   (-0,000004 · (LiftParamR1Vol/10) · (LiftParamR1Vol/10)
>> simpler:      -0.4E-7 · LiftParamR1Vol · LiftParamR1Vol
>
> Erledigt nicht der Compiler derartige Optimierungen von selbst?

Und hier ist ja genau das gegeben, was du oben geschrieben hast. Wenn 
LiftParamR1Vol < 10, dann muss das Ergebnis 0 sein, was durch die 
Optimierung nicht mehr der Fall wäre.

von Stefan F. (Gast)


Lesenswert?

Rolf M. schrieb:
> Und hier ist ja genau das gegeben, was du oben geschrieben hast. Wenn
> LiftParamR1Vol < 10, dann muss das Ergebnis 0 sein, was durch die
> Optimierung nicht mehr der Fall wäre.

Ach ja, ich habe nicht an die Klammern gedacht. Die Optimierung die 
Fpgakuechle vorschlug darf der Compiler gar nicht anwenden.

Was wäre, wenn LiftParamR1Vol ein float oder double wäre? Dürfte der 
Compiler den Ausdruck dann umstellen? Das Resultat wäre vermutlich mit 
einem anderen Rundungsfehler*, wäre das dem Compiler erlaubt?

*) Sorry, ich weiß nicht wie man die Fehler durch Beschränkungen des 
float/double Formates richtig nennt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A. S. schrieb:
> Der Compiler eliminiert ein (uint16/10)?

Nein, ich hatte, damit es vergleichbar zum "voroptimierten" Ausdruck 
ist, mal angenommen, dass der TE eigentlich wohl /10.0 gemeint hat.

Andernfalls hätte das alles eh keinen rechten Sinn.

von Fpgakuechle K. (Gast)


Lesenswert?

> Erledigt nicht der Compiler derartige Optimierungen von selbst?

(Irgend-) ein Compiler macht nur die Umsetzung v. C in Binärcode 
entsprechend der gerade zutreffenden Sprachdefinition resp LRM (Language 
Reference Manual) (und auch dabei macht ein Compiler Fehler resp. 
Eigenheiten).
Alles anderer ist Herstellerabhängiges 'Zuckerwerk', auf das man sich 
nicht verlassen kann.

> Ich weiß zum Beispiel mit Sicherheit, dass x/4 automatisch auf eine
> Shift Operation umgebaut wird, wenn x eine Integer Variable ist.

Das ist aber nur die halbe Wahrheit, wenn man die Vorzeichendarstellung 
betrachtet. Denn dann muß bei Zahlen mit Vorzeichen, eine '1' geschiftet 
werden, während bei denen ohne eine '0' eingeshiftet wird.

Und dann wäre auch noch zu klären, wie sich die Flags verhalten, eine 
Division könnte mehr oder anders Flags (Zero, Sign, Carries) beeinflußen 
als eine Shiftoperation (keine Flag-Änderung).

Und wenn wenn Operationen 'Automatisch' geschieht, würde ich diesen 
Compiler für den Produktivbetrieb sperren.

Schön wenn der Compiler ein paar Optimierungsmöglichkeiten findet, die 
sollte man aber m.E. in der nächsten Softwarerevision in den Code 
händisch einpflegen und dokumentieren.

Nochmals, das TO-Problem ist weniger ein 'C' als ein Numerik-Problem. 
Und dabei muß der Algorithmendesigner darauf achten, das er die 
Rechenoperationen korrekt (Für Zahlendarstellung) auswählt und sich 
nicht auf Architektur und Herstellerabhängige Compiler verlässt.

Es ist halt ein Problem, das es in der Schulmathematik keine Shifts gibt 
sondern eben nur Multiplikation / Addition und Informatiker dazu neigen 
ihre algos wie schulmathematiker zu notieren und nicht wie 'Numeriker', 
die eben mehr Operationen kennen als die vier Grundrechenarten. Bei den 
Crpto-Experten funktioniert das besser, die kennen 
'Formelzeichen'/Notationen auch für 'Bitoperarionen' wie XOR etc. bspw. 
https://commons.wikimedia.org/wiki/File:International_Data_Encryption_Algorithm_InfoBox_Diagram.svg

von A. S. (Gast)


Lesenswert?

Stefan ⛄ F. schrieb:
> Im obigen Programm sind aber Variablen beteiligt,

Ich hatte jetzt angenommen, dass das klar ist.

Stefan ⛄ F. schrieb:
> Ich weiß zum Beispiel mit Sicherheit, dass x/4 automatisch auf eine
> Shift Operation umgebaut wird, wenn x eine Integer Variable ist.

Das geht aber nur genau dann, wenn -1/4=-1. Ansonsten geht shift bei 
signed nicht.

von Fpgakuechle K. (Gast)


Lesenswert?

Jörg W. schrieb:
> wobei nicht ganz klar ist, was die
> Dividiererei und Multiplizierere mit den Zehnen soll.

vieleicht Runterskalieren von Eingangsgrößen um int-Übelaufe zu 
vermeiden?

Oder wenn es aus einem ADC stammt, die wegen niedrigen SNR verrauschten 
(und damit unbrauchbaren) lsb zu entfernen (dann wäre aber Bit-shift die 
bessere Wahl)

von Stefan F. (Gast)


Lesenswert?

Fpgakuechle K. schrieb:
> Alles anderer ist Herstellerabhängiges 'Zuckerwerk', auf das man sich
> nicht verlassen kann.

Ich verlasse mich durchaus darauf, dass jeder Compiler Ausdrücke wie

timeout = now + 3·24·60·60·1000

optimiert. Mit Compilern die das nicht tun, will ich gar nicht arbeiten. 
Das ist für mich keine Frage von 'Zuckerwerk' sondern ob es sich um ein 
vernünftiges Arbeitsmittel handelt, oder nicht.

Zwischen gar nicht optimieren und "alles" optimieren gibt es natürlich 
eine breite Grauzone deren Existenz man kennen sollte. Man sich da (wie 
du sagst) nicht automatisch auf alles verlassen, was man gerne hätte.

von Stefan F. (Gast)


Lesenswert?

A. S. schrieb:
> Das geht aber nur genau dann, wenn -1/4=-1.
> Ansonsten geht shift bei signed nicht.

Klar, ich hatte dabei nur nicht-negative ganze Zahlen im Sinn.

von Rolf M. (rmagnus)


Lesenswert?

Stefan ⛄ F. schrieb:
> Was wäre, wenn LiftParamR1Vol ein float oder double wäre? Dürfte der
> Compiler den Ausdruck dann umstellen? Das Resultat wäre vermutlich mit
> einem anderen Rundungsfehler*, wäre das dem Compiler erlaubt?

Gute Frage. Vereinfacht:
1
float foo(float arg)
2
{
3
    return arg / 10.0f * 10.0f;
4
}
Darf die Division und die Multiplikation entfernt werden oder nicht? Je 
nach Wert von arg kann das Ergebnis abweichen.

> *) Sorry, ich weiß nicht wie man die Fehler durch Beschränkungen des
> float/double Formates richtig nennt.

Ich kenne es auch als Rundungsfehler.

Fpgakuechle K. schrieb:
>> Ich weiß zum Beispiel mit Sicherheit, dass x/4 automatisch auf eine
>> Shift Operation umgebaut wird, wenn x eine Integer Variable ist.
>
> Das ist aber nur die halbe Wahrheit, wenn man die Vorzeichendarstellung
> betrachtet. Denn dann muß bei Zahlen mit Vorzeichen, eine '1' geschiftet
> werden, während bei denen ohne eine '0' eingeshiftet wird.

Dafür haben die mir bekannten Prozessorarchitekturen alle einen so 
genannten arithmetischen Rechtsshift, der genau das berücksichtigt. Beim 
AVR heißt der z.B. ASR. Statt von links immer 0-Bits einzufügen, kopiert 
der das MSB und erhält damit das Vorzeichen.

> Und dann wäre auch noch zu klären, wie sich die Flags verhalten, eine
> Division könnte mehr oder anders Flags (Zero, Sign, Carries) beeinflußen
> als eine Shiftoperation (keine Flag-Änderung).

Ja, je nach CPU-Architektur muss das ggf. berücksichtigt werden.

> Und wenn wenn Operationen 'Automatisch' geschieht, würde ich diesen
> Compiler für den Produktivbetrieb sperren.

Warum? Compiler führen allerhand krasses Zeug zur Optimierung durch. 
Solange das Ergebnis eines korrekten Programms sich dadurch nicht 
ändert, ist das auch kein Problem.

> Schön wenn der Compiler ein paar Optimierungsmöglichkeiten findet, die
> sollte man aber m.E. in der nächsten Softwarerevision in den Code
> händisch einpflegen und dokumentieren.

Die Grundidee ist eigentlich heutzutage, dass man seinen Code primär so 
schreibt, dass er gut lesbar ist und solche Detailoptimierungen eben dem 
Compiler überlässt, der das sowieso besser kann. Nicht selten macht das, 
was der Programmierer für eine Optimierung hält, den Code am Ende auf 
einer bestimmten Plattform sogar langsamer oder hat andere Auswirkungen, 
an die der nicht gedacht hat. Und es macht den Code komplizierter und 
damit unübersichtlicher.

> Nochmals, das TO-Problem ist weniger ein 'C' als ein Numerik-Problem.

Ja, wobei sich aus der Art, wie der C-Code geschrieben ist, bestimmte 
potenzielle numerische Probleme erst ergeben. LiftParamR1Vol/10 ist eben 
ein Integer, weil das in C so festgelegt ist. Wenn man das nicht 
berücksichtigt, kommt ggf. nicht das erwartete Ergebnis raus.

> Es ist halt ein Problem, das es in der Schulmathematik keine Shifts gibt
> sondern eben nur Multiplikation / Addition

An sich gibt's schon Shifts. Um mit 10 zu mulitiplizieren, muss ich nur 
eine 0 anhängen bzw. das Komma um eine Stelle verschieben. Keiner fängt 
da an, irgendwas zu berechnen.

> und Informatiker dazu neigen ihre algos wie schulmathematiker zu notieren
> und nicht wie 'Numeriker', die eben mehr Operationen kennen als die vier
> Grundrechenarten.

Das ist doch Unsinn. Gerade Informatiker haben früher "traditionell" 
immer versucht, Multiplikationen und Divisionen durch Shifts zu 
ersetzen. Heute sieht man davon ab, weil es die eigentliche Intention 
des Code schlechter darstellt. Außerdem muss ein Shift nicht unbedingt 
schneller sein als eine Mulitiplikation, wenn man da mal an einen Atmega 
mit MUL-Instruktion aber ohne Barrel-Shifter denkt.
Also überlässt man solche Mikrooptimierungen lieber dem Compiler.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Fpgakuechle K. schrieb:
> Jörg W. schrieb:
>> wobei nicht ganz klar ist, was die
>> Dividiererei und Multiplizierere mit den Zehnen soll.
>
> vieleicht Runterskalieren von Eingangsgrößen um int-Übelaufe zu
> vermeiden?

Wenn danach aber sowieso mit Gleitkomma weiter gerechnet wird, ist das 
Humbug.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jörg W. schrieb:
> Wenn danach aber sowieso mit Gleitkomma weiter gerechnet wird, ist das
> Humbug.

Der TO hat in seinem Eröffnungsposting das Ergebnis mit seinem 
Taschenrechner verglichen. Da ein Taschenrechner standardmäßig immer 
alles mit Gleitkomma rechnet, gehe ich davon aus, dass der TO die Folgen 
einer Integer-Division in C - nämlich grundsätzlich eine Abrundung - 
überhaupt nicht kennt.

Daraus - und auch aufgrund der gehäuften Fehler in nur einer Zeile C - 
schließe ich, dass der TO entweder ein absoluter C-Anfänger ist oder ein 
Troll. Die Wahrscheinlichkeit für letzteres steigt mit jedem Tag, an dem 
der TO sich nicht mehr meldet. Es kann aber auch sein, dass der TO wegen 
der massiven Kritik bereits früh ausgestiegen ist und sich deshalb nicht 
mehr meldet.

Ergo: Es hat überhaupt keinen Sinn mehr, über diese eine Zeile C weiter 
zu sinnieren, weder syntaktisch, noch philosophisch, noch inhaltlich. 
Das Ding ist kaputt, fertig. Es wurde auch schon alles nötige gesagt, um 
das Ding neu zu machen.

: Bearbeitet durch Moderator
von Fpgakuechle K. (Gast)


Lesenswert?

Rolf M. schrieb:
>
> Fpgakuechle K. schrieb:

> Dafür haben die mir bekannten Prozessorarchitekturen alle einen so
> genannten arithmetischen Rechtsshift, der genau das berücksichtigt. Beim
> AVR heißt der z.B. ASR. Statt von links immer 0-Bits einzufügen, kopiert
> der das MSB und erhält damit das Vorzeichen.

Naja, da kennst Du beispielsweise keine FPGA-Implementierung eines 
Algos, bei dem sich der FPGA-Designer selbst den Kopf um die 
Vorzeichenwahrung rep. nachgenerierung machen muß ;-)

>> Und wenn wenn Operationen 'Automatisch' geschieht, würde ich diesen
>> Compiler für den Produktivbetrieb sperren.
>
> Warum? Compiler führen allerhand krasses Zeug zur Optimierung durch.
> Solange das Ergebnis eines korrekten Programms sich dadurch nicht
> ändert, ist das auch kein Problem.

Naja das hängt wohl erheblich vom Bildungsniveau ab, was man als 'Krass' 
oder kalten Kaffee bezeichnet. Eigentlich sind alle Optimierungen, die 
ein Compiler so macht Niveau Gymnasium oder drunter. Manche sind 
lediglich wegen des zugrundeliegenden 'kompletten Ausprobierens' etwas 
zeitintensiv (bspw. Registerallocation), die kann man gerne automatisch 
machen. Das Problem bei den Optimierungen ist, das man sich nicht 100 
darauf verlassen kann, das sie tatsächlich aktiviert werden (siehe die 
Klammern) oben. Deshalb wird beispielsweise im Sicherheitskritischen 
Bereich (Medizintechnik; Luftfahrt) oft verlangt, das 
Compiler-Optimierung deaktiviert ist:
Beitrag "Wann Compiler-Optimierung in Embedded ausschalten"
Beitrag "Muss bei Misra Compiler Optimierung deaktiviert werden?"
Beitrag "Wie man volatile optimiert"

>> Schön wenn der Compiler ein paar Optimierungsmöglichkeiten findet, die
>> sollte man aber m.E. in der nächsten Softwarerevision in den Code
>> händisch einpflegen und dokumentieren.
>
> Die Grundidee ist eigentlich heutzutage, dass man seinen Code primär so
> schreibt, dass er gut lesbar ist und solche Detailoptimierungen eben dem
> Compiler überlässt, der das sowieso besser kann.

Gut lesbar ist der Code, bei dem man genau sieht, wann was das 
Rechenwerk mit welchen Seiteneffekten macht. Und die (Akademische) 
Grundidee heute ist, das man den Code vollständig spezifiziert und durch 
Tests verifiziert, das man sich um den Code dazwischen nicht kümmern muß 
und ihn ggf. auch von einen Horde Lemuren in Borneo schreiben lassen 
kann. Aber das nur als Nebenbemerkung.

Und eigentlich ist doch der gesamte thread ein Beispiel, das wie eine 
Formel lesbarer Code suboptimal übersetzt wird, weil man der 
Compiler-optimierung (C wählt automatisch den besteb Datentyp) blind 
vertraut.

Wie kann man da noch ernsthaft behaupten, es genüge den Code schön 
hinzuschreiben und der Compiler tut wie ein gedankenlesender 
Hofalchemist den Rest bei der wundersamen Transformation von tauben Erz 
in edles Gold?!

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Fpgakuechle K. schrieb:
> Wie kann man da noch ernsthaft behaupten, es genüge den Code schön
> hinzuschreiben und der Compiler tut wie ein gedankenlesender
> Hofalchemist den Rest bei der wundersamen Transformation von tauben Erz
> in edles Gold?!

Ja,aeh - Nein, ein C compiler kann das natuerlich nicht. Aber ein Rust 
Compiler, deeer kann das (also zumindest, wenn er die richtige 
Versionsnummer hat...)

Duck&wech
WK

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stefan ⛄ F. schrieb:
> Was wäre, wenn LiftParamR1Vol ein float oder double wäre? Dürfte der
> Compiler den Ausdruck dann umstellen? Das Resultat wäre vermutlich mit
> einem anderen Rundungsfehler*, wäre das dem Compiler erlaubt?

Für die meisten Anwendunen ist es ok wenn double + und * assoziativ 
sind, und GCC kennt dafür -fassociative-math.

Und meist ist auch -ffast-math in Ordnung, was einen ganzen Zoo von 
Optionen setzt wie -fassociative-math, -ffinite-math, -fno-signed-zeros 
etc.  Leider hat das keinen Einfluss auf Lib-Funktionen weil es keine 
Multilib-Optionen sind.

von Rolf M. (rmagnus)


Lesenswert?

Fpgakuechle K. schrieb:
>>> Und wenn wenn Operationen 'Automatisch' geschieht, würde ich diesen
>>> Compiler für den Produktivbetrieb sperren.
>>
>> Warum? Compiler führen allerhand krasses Zeug zur Optimierung durch.
>> Solange das Ergebnis eines korrekten Programms sich dadurch nicht
>> ändert, ist das auch kein Problem.
>
> Naja das hängt wohl erheblich vom Bildungsniveau ab, was man als 'Krass'
> oder kalten Kaffee bezeichnet.

Manche Optimierungen sind durchaus nicht trivial. Aber was ich damit nur 
ausdrücken wollte, ist, dass da deutlich mehr gemacht wird als man 
gemeinhin erwartet.

> Das Problem bei den Optimierungen ist, das man sich nicht 100
> darauf verlassen kann, das sie tatsächlich aktiviert werden (siehe die
> Klammern) oben.

Natürlich nicht.

>> Die Grundidee ist eigentlich heutzutage, dass man seinen Code primär so
>> schreibt, dass er gut lesbar ist und solche Detailoptimierungen eben dem
>> Compiler überlässt, der das sowieso besser kann.
>
> Gut lesbar ist der Code, bei dem man genau sieht, wann was das
> Rechenwerk mit welchen Seiteneffekten macht.

Bei Assembler mag das zutreffen. Aber Hochsprachen abstrahieren das ganz 
bewusst. Dort gilt Code als gut lesbar, der beschreibt, was die 
Intention ist, nicht wie es nachher in der Maschine umgesetzt wird. Das 
ist ja der ganze Sinn der Abstraktion. Genauso, wie es mich nicht im 
Detail interessiert, wie die Mechanik-Teile und Steuergeräte in meinem 
Auto genau interagieren, damit der Motor tut, was er soll. Ich trete 
einfach das Gaspedal, und es beschleunigt.

> Und die (Akademische) Grundidee heute ist, das man den Code vollständig
> spezifiziert und durch Tests verifiziert, das man sich um den Code
> dazwischen nicht kümmern muß und ihn ggf. auch von einen Horde Lemuren in
> Borneo schreiben lassen kann. Aber das nur als Nebenbemerkung.

Das funktioniert so allerdings nur, wenn man den Code nachher nicht mehr 
anfassen muss.
Ob es jetzt der aktuellen akademischen Idee widerspricht, weiß ich 
nicht, aber in der täglichen Praxis ist die Grundidee eher, dass Code 
primär für den Menschen geschrieben wird, weil er in der Regel öfter von 
Menschen gelesen als geschrieben wird. Und um das für den Computer zu 
optimieren, hab ich den Compiler.

> Und eigentlich ist doch der gesamte thread ein Beispiel, das wie eine
> Formel lesbarer Code suboptimal übersetzt wird, weil man der
> Compiler-optimierung (C wählt automatisch den besteb Datentyp) blind
> vertraut.

Nein, das ist höchstens ein falsches Sprachverständnis, denn wie schon 
beschrieben wurde, darf der Compiler das gar nicht optimieren.

> Wie kann man da noch ernsthaft behaupten, es genüge den Code schön
> hinzuschreiben und der Compiler tut wie ein gedankenlesender
> Hofalchemist den Rest bei der wundersamen Transformation von tauben Erz
> in edles Gold?!

Sowas behauptet keiner. Vielmehr schreibt man hin, was man machen will, 
und der Compiler hat die Aufgabe, dann einen effizienten Weg zu finden, 
das umzusetzen. Und wenn ich x * 10 ausrechnen will, schreibe ich x * 10 
hin und nicht (x << 3) + (x << 1), nur weil ich mir erhoffe, dass das 
auf meiner Zielarchitektur drei Taktzyklen spart. Wenn dem so ist, darf 
das der Compiler gerne so umsetzen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Fpgakuechle K. schrieb:
> Deshalb wird beispielsweise im Sicherheitskritischen Bereich
> (Medizintechnik; Luftfahrt) oft verlangt, das Compiler-Optimierung
> deaktiviert ist

Die Behauptung in diesen Threads scheinen aber eher "Hörensagen" denn 
Tatsache zu sein.

Ich kann mich übrigens auch nicht dran erinnern, dass zumindest in MISRA 
irgendwas derartiges stehen würde. Gerade mit aktuellen 
MCU-Architekturen wäre es völlig unsinnig, auf Compileroptimierungen 
verzichten zu wollen. Das mag bei Intel-Hardware, die sowieso versucht, 
den Compiler gleich mal noch überholen zu wollen (mit entsprechendem 
Energieaufwand) anders gewesen sein, aber wenn ich bei einem ARM die 
Optimierung weglasse, rennt das Teil nur mit einem Bruchteil der 
möglichen Geschwindigkeit.

> Das Problem bei den Optimierungen ist, das man sich nicht 100 darauf
> verlassen kann, das sie tatsächlich aktiviert werden (siehe die
> Klammern) oben.

Du kannst dich nicht drauf verlassen, dass der Compiler irgendwas in der 
von dir hingeschriebenen Reihenfolge oder Anordnung rechnen lässt. Es 
gilt die "as if"-Regel: das Ergebnis darf sich nur nicht von dem 
unterscheiden, welches herauskommt, wenn die abstrakte Maschine es genau 
so wie geschrieben abarbeitet.

"Premature optimization is the root of all evil." (Donald Knuth)

Man sollte die Dinge so hinschreiben, wie sie ein Mensch am besten lesen 
und erfassen kann, nicht so, wie man denkt, dass sie bestmöglich 
"voroptimiert" sind.

Wenn es irgendwo wirklich drauf ankommt (innere Schleife, die sehr 
häufig durchlaufen wird), dann kann man allemal noch versuchen, mit ein 
paar derartiger Mikrooptimierungen für eine bestimmte Compilerversion 
ein wenig gut zu machen – auf Kosten der Arbeit, dass man es für einen 
anderen Compiler oder die nächste Version ggf. neu evaluieren muss.

Wenn man alle Optimierungen abschaltet, ist man natürlich fein raus: 
dann kommt es ja ganz offensichtlich auf jegliche Art von Performance 
schlicht gar nicht an. ;-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Jörg W. schrieb:
> Fpgakuechle K. schrieb:
>
>> Deshalb wird beispielsweise im Sicherheitskritischen Bereich
>> (Medizintechnik; Luftfahrt) oft verlangt, das Compiler-Optimierung
>> deaktiviert ist
>
> Die Behauptung in diesen Threads scheinen aber eher "Hörensagen" denn
> Tatsache zu sein.

Das nehme ich auch an. Meines Erachtens erreicht man durch Abschalten 
der Optimierung das Gegenteil von dem, was man möchte.

Das Abschalten der Optimierung

- unterdrückt oft Warnungen, weil der Compiler Probleme erst bei höheren 
Optimierungsstufen erkennt.

- verschleiert oft Programmierfehler, weil sie in der "dummen" Version 
nicht auftreten, weil das Programm zum Beispiel ein ganz anderes 
Zeitverhalten zeigt oder die Datenstruktur im RAM anders anordnet.

Erst wenn man die Optimierung einschaltet, spuckt der Compiler plötzlich 
Warnungen aus, an die man im Traum nicht gedacht hat. Zudem offenbaren 
sich plötzlich Programmierfehler wie Race Conditions, Buffer-Overflows 
oder - ganz trivial - fehlende volatile-Deklarationen von in ISRs 
benutzten globalen Variablen.

Von daher sehe ich das so: Ein "gutes Programm" ist eines, welches von 
der Optimierungsstufe, mit der es übersetzt wurde, komplett unabhängig 
ist. Man sollte daher, wenn man sein Programm testet, dieses immer mit 
einer Optimierung (z.B. -Os oder -O2) übersetzen.

: Bearbeitet durch Moderator
von Joachim B. (jar)


Lesenswert?

Frank M. schrieb:
> dass der TO entweder ein absoluter C-Anfänger ist oder ein
> Troll. Die Wahrscheinlichkeit für letzteres steigt mit jedem Tag, an dem
> der TO sich nicht mehr meldet.

und weil zu keiner Zeit auf ',' vs '.' eingegangen wird!

von Rolf M. (rmagnus)


Lesenswert?

Frank M. schrieb:
> Von daher sehe ich das so: Ein "gutes Programm" ist eines, welches von
> der Optimierungsstufe, mit der es übersetzt wurde, komplett unabhängig
> ist.

Ich würde "gutes" hier durch "korrektes" ersetzen. Denn wenn ein 
Programm nach dem Einschalten irgendwelcher Optimierungen nicht mehr 
funktioniert, liegt das
nahezu immer daran, dass es einen Fehler enthält.

von Erich (Gast)


Lesenswert?

Jörg W. schrieb:
> Fpgakuechle K. schrieb:
>> Deshalb wird beispielsweise im Sicherheitskritischen Bereich
>> (Medizintechnik; Luftfahrt) oft verlangt, das Compiler-Optimierung
>> deaktiviert ist.
>>
   Das ist falsch.
   Aber es werden erprobte oder zugelassene Versionen der Compiler 
verlangt.
   Naturgemäß können das keine ganz neuen sein.
   Ggf. muß auch die Optimierungsstufe erprobt sein, oder weitere 
Optionen.
>
>
> Die Behauptung in diesen Threads scheinen aber eher "Hörensagen" denn
> Tatsache zu sein.
>
   Das ist grossteils richtig.
>
>
> Ich kann mich übrigens auch nicht dran erinnern, dass zumindest in MISRA
> irgendwas derartiges stehen würde.
>
   Lt. MISRA ist die Verwendung von float nicht zulässig.

   Über der Wertebereich aller Variablen und möglichen Ergebnisse von 
Berechnungen hat man sich vorher mehr Gedanken zu machen.
Insbesondere was Min und Max Werte sowie Über- oder Unterläufe betrifft.
>
Gruss

von Philipp Klaus K. (pkk)


Lesenswert?

>    Lt. MISRA ist die Verwendung von float nicht zulässig.

Quelle?

Den alten MISRA-C:2004 findet man im Netz, der war viel strenger als der 
aktuelle MISRA-C:2021 (z.b. war goto verboten), aber selbst der sagt 
nicht s gegen float im Allgemeinen (verbietet aber float als 
Schleifenzähler für for - genauso wie double und long double).

: Bearbeitet durch User
von Erich (Gast)


Lesenswert?

Philipp Klaus K. schrieb:
Erich schrieb:
>>    Lt. MISRA ist die Verwendung von float nicht zulässig.

> Quelle?

Ok, vielleicht war das dann "im Prinzip" erlaubt, aber mit solch hohen 
Einschränkungen wie...
 "Use of floating-point arithmetic shall be documented."
 "conforms to IEEE 754"
 "preserve precision when converting integral values to floating-point 
type"
 "do not use object representations to compare floating-point values"
Damit wäre der Review-Aufwand quasi jeweils höher gewesen als der Nutzen 
in damaligem Projekt, sodaß wir diese Vereinbarung selbst uns auferlegt 
haben (keine float im Projekt zu verwenden).

von Philipp Klaus K. (pkk)


Lesenswert?

Erich schrieb:
> Philipp Klaus K. schrieb:
> Erich schrieb:
>>>    Lt. MISRA ist die Verwendung von float nicht zulässig.
>
>> Quelle?
>
> Ok, vielleicht war das dann "im Prinzip" erlaubt, aber mit solch hohen
> Einschränkungen wie...

Hohe Einschränkungen sind das, soweit ich sehe, nicht. Aber ich habe 
nicht wirklich MISRA-C-Erfahrung.

>  "Use of floating-point arithmetic shall be documented."

Aber wohl nicht jeder Einzelfall, sondern nur, dass das Programm 
insgeasmt es nutzt?

>  "conforms to IEEE 754"

Das erfüllen die meisten Compiler (es gibt ja auch ein Marko im 
C-Standard, dass dies anzeigt). Der SDCC ist raus, und bei GCC darf man 
kein -ffast-math verwenden, aber sonst ist das keine echte einschränlung

>  "preserve precision when converting integral values to floating-point
> type"

Damit fällt der Cast von int32_t nach float weg (aber nicht der von 
int16_t nach float und auch nicht der von int32_t nach double).

>  "do not use object representations to compare floating-point values"

Warum sollte man das in einem normalen Programm jemals machen wollen? C 
hat == (was im Kontext von float auch nicht ganz unproblematisch ist, 
aber trotzdem viel besser als memcmp).

von Joachim B. (jar)


Lesenswert?

Werz schrieb:
> -0,000004

Werz schrieb:
> + 0,0405

welcher C-Compiler benutzt ',' für floats!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joachim B. schrieb:
> welcher C-Compiler benutzt ',' für floats!

Guten Morgen! Wo warst du denn den Rest des Threads lang? Bereits das 
Lesen des zweiten Beitrags hätte dich erleuchten können, dass das wohl 
so ziemlich jedem Mitleser hier aufgefallen ist, allerdings interessiert 
sich der TE gar nicht mehr dafür (oder traut sich nicht mehr raus).

Davon abgesehen, zu Beginn der ganzen i18n-Geschichte hat es IBM auf AIX 
wohl tatsächlich mal geschafft, dass der C-Compiler in deutscher 
Umgebung Kommas statt Punkte haben wollte. :-o

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Philipp Klaus K. schrieb:
> C hat == (was im Kontext von float auch nicht ganz unproblematisch ist

Daher ist der Vergleich von Gleitkommazahlen auf (Un-)Gleichheit wohl 
tatsächlich in MISRA nicht statthaft.

Wobei ich persönlich dafür bin, dass man von dieser Regel abweichen 
dürfen sollte, wenn ein Vergleich auf 0.0 erfolgt und diese 0.0 das 
Ergebnis einer expliziten Zuweisung oder (auch impliziten) 
Initialisierung ist. Die 0.0 ist immer eindeutig darstellbar und daher 
auch exakt vergleichbar.

von Stefan F. (Gast)


Lesenswert?

Philipp Klaus K. schrieb:
> Aber wohl nicht jeder Einzelfall, sondern nur, dass das Programm
> insgeasmt es nutzt?

Also wenn ich float (oder double) irgendwo bei der Berechnung von 
Geldbeträgen benutzen würde, müsste ich ganz sicher jeden Fall einzeln 
detailliert dokumentieren, samt Analyse der möglichen negativen Folgen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan ⛄ F. schrieb:
> Also wenn ich float (oder double) irgendwo bei der Berechnung von
> Geldbeträgen benutzen würde, müsste ich ganz sicher jeden Fall einzeln
> detailliert dokumentieren

Oh, dein Konto hat einen Füllstand, der 14 signifikante Stellen 
übersteigt?

Aber ich glaube, dann käme es auf die letzten falsch gerundeten Cents 
auch nicht mehr an. :-)

von Erich (Gast)


Lesenswert?

Jörg W. schrieb:
> Die 0.0 ist immer eindeutig darstellbar und daher
> auch exakt vergleichbar.

Das stimmt nicht ganz.
Es gibt die +0.0 UND die -0.0
https://de.wikipedia.org/wiki/IEEE_754#Null
Also wären jeweils zwei Vergleiche nötig, oder Vergleich ohne das sign.
Ist das im jeweiligen Compiler / Library so umgesetzt? Wirklich?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Erich schrieb:
> Es gibt die +0.0 UND die -0.0

Ich schrieb: wenn die 0.0 durch explizite Zuweisung oder (auch 
implizite) Initialisierung entstanden ist, dann ist das sicher (sie hat 
dann das Bitmuster, bei dem alle Bits 0 sind). Das schließt den Fall, 
dass da jemand -0.0 zugewiesen hat, folglich aus. Davon abgesehen, habe 
ich noch keinen gesehen, der sowas geschrieben hätte. Damit ergibt sich 
der Fall -0.0 also lediglich als Ergebnis einer Berechnung, und bei dem 
würde ich mich sowieso nie drauf verlassen wollen, dass exakt 0 
rauskommt, da gebe ich MISRA völlig Recht.

Übrigens, per definitionem gilt, dass +0.0 == -0.0 ist. Das setzen die 
Compiler auch so um.

von Stefan F. (Gast)


Lesenswert?

Jörg W. schrieb:
> Oh, dein Konto hat einen Füllstand, der 14 signifikante Stellen
> übersteigt?

Meins nicht, aber die Kunden für die ich entwickle legen Wert darauf.

Wenn das Finanzamt nur einen Cent Rundungsfehler in der Jahresbilanz 
entdeckt, werden die Leute manchmal sehr nervig.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Stefan ⛄ F. schrieb:
> Meins nicht, aber die Kunden für die ich entwickle legen Wert darauf.

Billiardäre? ;-)

OK, allerdings dürfte das auch kein Fall für MISRA sein, und für sowas 
gibt's eh "bignum"-Bibliotheken.

von Rolf M. (rmagnus)


Lesenswert?

Jörg W. schrieb:
> Wobei ich persönlich dafür bin, dass man von dieser Regel abweichen
> dürfen sollte, wenn ein Vergleich auf 0.0 erfolgt und diese 0.0 das
> Ergebnis einer expliziten Zuweisung oder (auch impliziten)
> Initialisierung ist. Die 0.0 ist immer eindeutig darstellbar und daher
> auch exakt vergleichbar.

Gilt das nicht an sich für jeden Wert, den man direkt (ohne Berechnung) 
hinein geschrieben hat?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Gilt das nicht an sich für jeden Wert, den man direkt (ohne Berechnung)
> hinein geschrieben hat?

Bin ich mir nicht so völlig sicher, während ich mir beim Bitmuster 0 
sicher bin. :)

Für andere Werte ist es aber auch weniger wichtig. Wenn ich weiß, dass 
irgendein Koeffizient beim ersten Programmlauf berechnet wird und dabei 
auf jeden Fall etwas anderes als 0.0 bekommt, dann kann ich eben diesen 
(durch die Initialisierung globaler Variablen erzeugten) Wert 0.0 als 
Vergleich für "muss noch berechnet werden" benutzen, statt mir noch ein 
Flag extra mitschleifen zu müssen "foo_has_been_initialized".

: Bearbeitet durch Moderator
von Joachim B. (jar)


Lesenswert?

Jörg W. schrieb:
> Guten Morgen! Wo warst du denn den Rest des Threads lang?

ich habe den ganzen Thread mehrfach gelesen, was willst du mir 
mitteilen?
ging es nicht um einen 32-bit AVR flash microcontroller und C?
Ist in C nicht das ',' ein Datentrenner?

Jörg W. schrieb:
> allerdings interessiert
> sich der TE gar nicht mehr dafür

eben das wollte ich nur noch mal klarstellen, denn es wird immer noch 
palavert!

eigentlich könnte ein Schloß an den Thread!

: Bearbeitet durch User
von A. S. (Gast)


Lesenswert?

Joachim B. schrieb:
> was willst du mir mitteilen?
> ging es nicht um einen 32-bit AVR flash microcontroller und C?
> Ist in C nicht das ',' ein Datentrenner?

Ja. Wie in der ersten Antwort hingewiesen:

Hi schrieb:
> "." statt "," wäre schon mal ein guter Anfang ;)

von A. S. (Gast)


Lesenswert?

Jörg W. schrieb:
> dann kann ich eben diesen
> (durch die Initialisierung globaler Variablen erzeugten) Wert 0.0 als
> Vergleich für "muss noch berechnet werden" benutzen, statt mir noch ein
> Flag extra mitschleifen zu müssen "foo_has_been_initialized".

Ja, könnte man machen. Aber wozu? Wichtig ist, dass jeder MISRA-Nutzer 
das Problem kennt und versteht. Das würde durch eine Erweiterung "aber 
mit 0.0 ist das dann unter den und den Bedingungen erlaubt" nicht 
einfacher.

Wenn Du dann eine plausible Anwendung für 0.0 hast und sicherstellen 
kannst, dass dies zu keinen Problemen führt, dann kannst Du es ja mit 
kurzem Kommentar nutzen. Solange Du weißt, was Du tust.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A. S. schrieb:
> Wenn Du dann eine plausible Anwendung für 0.0 hast und sicherstellen
> kannst, dass dies zu keinen Problemen führt, dann kannst Du es ja mit
> kurzem Kommentar nutzen. Solange Du weißt, was Du tust.

Führt dann dazu, dass irgendwann die Hälfte aller Kommentare nicht mehr 
den eigentlichen Codefluss kommentiert, sondern die diversen 
MISRA-Ausnahmen.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joachim B. schrieb:
> eben das wollte ich nur noch mal klarstellen, denn es wird immer noch
> palavert!

Ja, aber nur du musstest nach 3 Tagen nochmal Dinge kommentieren, die 
bereits ein paar Minuten nach dem Posting klar waren. Der Rest der 
Diskussion dreht sich um andere Dinge (die keineswegs uninteressant 
sind), und eben daher gehört da kein Schloss davor (solange die 
Diskussion gesittet bleibt, aber da sehe ich gerade gar keine Gefahr).

Beitrag #6815094 wurde vom Autor gelöscht.
von 900ss (900ss)


Lesenswert?

Jörg W. schrieb:
> Fpgakuechle K. schrieb:
>
>> Deshalb wird beispielsweise im Sicherheitskritischen Bereich
>> (Medizintechnik; Luftfahrt) oft verlangt, das Compiler-Optimierung
>> deaktiviert ist
>
> Die Behauptung in diesen Threads scheinen aber eher "Hörensagen" denn
> Tatsache zu sein

Ack. Wir mussten gerade tagelang Fehler suchen, die sich zeigten, als 
darauf gedrängt wurde, jede nur mögliche Optimierung zu aktivieren, die 
Laufzeit spart. War eigenlich wegen der Laufzeit nicht notwendig. Es ist 
für eine Payload eines Satelliten. Aber man lernt dabei jede Menge, auch 
die Leute, die den Assermbler nicht beherrschten, haben ihn dann 
kennengelernt  ;)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

900ss D. schrieb:
> Wir mussten gerade tagelang Fehler suchen, die sich zeigten, als darauf
> gedrängt wurde, jede nur mögliche Optimierung zu aktivieren, die
> Laufzeit spart.

Worauf habt ihr denn davor optimiert?

"Jegliche mögliche Optimierung bezüglich Laufzeit" klingt ja nach sowas 
wie -O3, wo massiv Code aufgebläht werden darf, wenn es nur Laufzeit 
spart (loop unrolling etc.). Ansonsten nimmt man bei MCUs ja eher -Os: 
"optimiere alles, was nicht mehr Code produziert" (wobei geringere 
Codegröße ja typisch auch erstmal Laufzeit spart).

Ansonsten: wer mit -O0 entwickelt, muss sich nicht wundern, wenn dann 
beim Einschalten der Optimierung plötzlich alle möglichen Warnungen oder 
Fehler auftauchen, die zwar schon immer in der Software waren, aber 
vorher noch keiner bemerkt hatte.  Wurde im Thread schonmal geschrieben, 
aber dieses "ich compilier erstmal mit -O0, damit ich schön im Debugger 
durch steppen kann" ist leider so verbreitet, dass man das ruhig nochmal 
erwähnen sollte. Wer mit -O0 entwickelt, debuggt zweimal.

Aber ja, bei einem Controller sollte man den Assemblercode zumindest 
rudimentär zu lesen in der Lage sein. Insofern haben eure Entwickler ja 
was Nützliches dabei gelernt. ;-)

von Stefan F. (Gast)


Lesenswert?

Jörg W. schrieb:
> Billiardäre? ;-)

Keine Ahnung wie hoch die Umsätze von Vodafone und der Allianz 
Versicherung sind. Jedenfalls hoch genug, dass sie solche Anforderungen 
stellen können.

> für sowas gibt's eh "bignum"-Bibliotheken.

Genau. Schade dass sie nicht zum Standardumfang von C gehören, das wäre 
manchmal praktisch. Bisher habe ich auf µC immer versucht, sie zu 
umgehen bzw. weg zu argumentieren. Aber bei Business-Server Anwendungen 
kann ich das nicht machen.

von Fpgakuechle K. (Gast)


Lesenswert?

Rolf M. schrieb:

> Gilt das nicht an sich für jeden Wert, den man direkt (ohne Berechnung)
> hinein geschrieben hat?

nur bei normalisierten float, unnormalisierte sind nicht eineindeutig.

80 beispielsweise wäre normalisiert 0.8E02, auch wenn man in den C-Code 
80 schreibt. Die Frage ist ob es sowas wie 'ohne berechnung 
reinschreiben' überhaupt gibt, wenn ein Compiler zwischen dem User und 
der Maschine steht, dessen Umwandlungen weder vom User beherrscht noch 
überprüft werden.

von Philipp Klaus K. (pkk)


Lesenswert?

Stefan ⛄ F. schrieb:
> Jörg W. schrieb:
>> Billiardäre? ;-)
>
> Keine Ahnung wie hoch die Umsätze von Vodafone und der Allianz
> Versicherung sind. Jedenfalls hoch genug, dass sie solche Anforderungen
> stellen können.
>
>> für sowas gibt's eh "bignum"-Bibliotheken.
>
> Genau. Schade dass sie nicht zum Standardumfang von C gehören, das wäre
> manchmal praktisch. Bisher habe ich auf µC immer versucht, sie zu
> umgehen bzw. weg zu argumentieren. Aber bei Business-Server Anwendungen
> kann ich das nicht machen.

Ich hätte jetzt eher geschätzt, dass im Finanzwesen dezimale 
Gleitkommazahlen (nach ISO/IEC TS 18661-2:2015 bzw. ISO C23) verwendet 
werden.

Ein bignum (im Sinne von zur Laufzeit veränderlicher Größe) wird es wohl 
in C23 nicht geben, aber dafür Ganzzahltypen mit vom Programmierer zur 
Compilezeit wählbarer Größe.

von Stefan F. (Gast)


Lesenswert?

Philipp Klaus K. schrieb:
> Ich hätte jetzt eher geschätzt, dass im Finanzwesen dezimale
> Gleitkommazahlen (nach ISO/IEC TS 18661-2:2015 bzw. ISO C23) verwendet
> werden.
> Ein bignum (im Sinne von zur Laufzeit veränderlicher Größe) wird es
> wohl in C23 nicht geben

Jetzt hast du mich abgehängt, ich weiß nicht mehr worum es geht.

In MariaDB Datenbanken benutzen wir für Geldbeträge meistens den 
Datentyp decimal(10,2). In Java dementsprechend BigDecimal, in Go 
Money*.

Money ist eine Struktur aus:
string currency_code
int64 units // The whole units of the amount.
int32 nanos // Number of nano (10^-9) units of the amount.

von Philipp Klaus K. (pkk)


Lesenswert?

Stefan ⛄ F. schrieb:
> Philipp Klaus K. schrieb:
>> Ich hätte jetzt eher geschätzt, dass im Finanzwesen dezimale
>> Gleitkommazahlen (nach ISO/IEC TS 18661-2:2015 bzw. ISO C23) verwendet
>> werden.
>> Ein bignum (im Sinne von zur Laufzeit veränderlicher Größe) wird es
>> wohl in C23 nicht geben
>
> Jetzt hast du mich abgehängt, ich weiß nicht mehr worum es geht.

Die Datentypen _Decimal32, _Decimal64, und _Decimal128. Diese 
implementieren dezimale Gleitkommazahlen (während float, double und long 
double üblicherweise binäre Gleitkommazahlen implementieren).

Bei dezimalen Gleitkommazahlne ist üblicherweise der Exponent eine 
Binärzahl, die Mantisse aber eine Dezimalzahl. Für die Mantisse wird oft 
DPD verwendet, da es etwas speicherplatzeffizienter als BCD ist (10 Bits 
je 3 Dezimalstellen vs. 4 Bits je Dezimalstelle).

Wobei wir hier langsam off-topic werden, da ich vermute, dass diese 
Formate auf µC kaum eingesetzt werden (z.B. SDCC implementiert sie 
nicht).

Unter bignum dagegen verstand ich einen Typ, der beliebig große Zahlen 
speichern kann, wobei dann unter Umständen dynamisch Speicher allokiert 
werden muss.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Philipp Klaus K. schrieb:
> Wobei wir hier langsam off-topic werden, da ich vermute, dass diese
> Formate auf µC kaum eingesetzt werden (z.B. SDCC implementiert sie
> nicht).

Ich glaube auch nicht, dass jemand mit Billionenbeträgen auf einer MCU 
jongliert. ;-)

von Joachim B. (jar)


Lesenswert?

Jörg W. schrieb:
> Ich glaube auch nicht, dass jemand mit Billionenbeträgen auf einer MCU
> jongliert. ;-)

ich glaube aber das es genügend User gibt die billionen von µs auf einer 
MCU handeln wollen als Wartezeit!

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joachim B. schrieb:
> ich glaube aber das es genügend User gibt die billionen von µs auf einer
> MCU handeln wollen als Wartezeit!

10^6 s?  Das sind knapp 2 Wochen, und ob's dabei auf die letzte µs 
ankommt?

von Philipp Klaus K. (pkk)


Lesenswert?

Jörg W. schrieb:
> Joachim B. schrieb:
>> ich glaube aber das es genügend User gibt die billionen von µs auf einer
>> MCU handeln wollen als Wartezeit!
>
> 10^6 s?  Das sind knapp 2 Wochen, und ob's dabei auf die letzte µs
> ankommt?

Wenn man z.B. einen Zähler hat, der mit einem 1 kHz-Interrupt 
hochgezählt wird, braucht man die letzte ms, damit überhaupt immer 
weitergezählt wird.

Daher (und weil es effizienter ist) würde ich eher einen unsigned long 
long nehmen, statt einer Gleitkommazahl.

von Rolf M. (rmagnus)


Lesenswert?

Fpgakuechle K. schrieb:
> Rolf M. schrieb:
>
>> Gilt das nicht an sich für jeden Wert, den man direkt (ohne Berechnung)
>> hinein geschrieben hat?
>
> nur bei normalisierten float, unnormalisierte sind nicht eineindeutig.

Kann man das bei der Eingabe überhaupt irgendwie wählen?

> 80 beispielsweise wäre normalisiert 0.8E02, auch wenn man in den C-Code
> 80 schreibt. Die Frage ist ob es sowas wie 'ohne berechnung
> reinschreiben' überhaupt gibt, wenn ein Compiler zwischen dem User und
> der Maschine steht, dessen Umwandlungen weder vom User beherrscht noch
> überprüft werden.

Allerdings würde ich davon ausgehen, dass die selbe Umwandlung auch zum 
selben Ergebnis führt. Also bei einem:
1
x = 123.456;
und einem
1
if (x == 123.456)
die Wandlung des eingegebenen Literals "123.456" in den Float-Typ in den 
beiden Fällen nicht unterschiedliche Ergebnisse produziert. Und ich 
würde davon ausgehen, dass das nicht nur für 123.456, sondern für jeden 
Wert gilt. Die Frage ist eben nur, ob das garantiert ist.

von 900ss (900ss)


Lesenswert?

Jörg W. schrieb:
> Worauf habt ihr denn davor optimiert?

Auf -O0 :) Es war wirklich viel der Debugger nötig. Und den Code erst 
auf dem PC zu testen war zu aufwendig da sehr viel HW Zugriffe vorhanden 
sind. Ich hatte in meinem Teil nur eine fiese Stelle. Ein memcpy(x,y,4) 
wurde durch eine 32-bit load Instruktion ersetzt. Peng! Die Adressen 
waren nicht aligned. Nachdem der Pointer auf uint_8 zeigte anstatt auf 
uint32_t hat er das memcpy nicht mehr ersetzt ;) Klar mein Fehler durch 
falschen Pointertyp.

Jörg W. schrieb:
> Ansonsten nimmt man bei MCUs ja eher -Os:
Es ist ein 32-Bit "Bolide" :)

Jörg W. schrieb:
> Aber ja, bei einem Controller sollte man den Assemblercode zumindest
> rudimentär zu lesen in der Lage sein. Insofern haben eure Entwickler ja
> was Nützliches dabei gelernt. ;-)

Ich fürchte, dass sie das schon wieder vergessen haben. In der Regel 
interessiert es sie nicht :(

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

900ss D. schrieb:
> Jörg W. schrieb:
>> Worauf habt ihr denn davor optimiert?
>
> Auf -O0 :)

Uff.

> Es war wirklich viel der Debugger nötig. Und den Code erst
> auf dem PC zu testen war zu aufwendig da sehr viel HW Zugriffe vorhanden
> sind.

Mein Tipp: lernen, wie man optimierten Code debuggt. Das geht. Als 
Embedded Programmer muss man sich halt kleine Hilfen einfallen lassen. 
Wenn man unbedingt an einer ganz bestimmten Stelle einen Breakpoint 
setzen können will, tut's ein
1
__asm volatile("nop":::);

Noch krasser wäre
1
__asm volatile("nop":::"memory");

Damit zwingt man den Compiler, die gemäß der abstrakten Maschine an 
dieser Stelle abzuarbeitenden Speicherzugriffe auch wirklich alle vorher 
zu generieren, statt irgendwas darüber hinweg zu cachen.

Auf so einen NOP kann man halt immer einen Breakpoint werfen.

Zwischenvariablen zum Angucken im Debugger kann man sich temporär als 
volatile reinnageln.

Wenn man eine ganze Serie von Zwischenwerten ablegen lassen will, um 
sich im Debugger so eine Art "value history" ansehen zu können, tut's 
ein einfacher Ringbuffer:
1
#define DD_CNT 32
2
uint8_t debugdata[DD_CNT];
3
int debugidx;
4
5
...
6
   debugdata[debugidx] = my_interesting_value;
7
   if (++debugidx == DD_CNT) debugidx = 0;
8
...

Das Ganze als globale Variablen hinterlegt, wird es auch nicht 
wegoptimiert (ok, zumindest nicht ohne -flto).

So kann man sich noch weitere Tricks bauen. Was wir auch stets in all 
unseren MCU-Projekten hatten: ein paar GPIOs (4 sind nett, ansonsten so 
viele, wie gerade entbehrlich sind), um irgendwelche Traces auf dem 
Logikanalysator rauszuwerfen. Bei 4 Bit kannst du sogar ganze 
Nachrichten in Form von Nibbels rauspusten ;-) und das, ohne nennenswert 
an Ausführungsgeschwindigkeit einzubüßen.

> Jörg W. schrieb:
>> Ansonsten nimmt man bei MCUs ja eher -Os:
> Es ist ein 32-Bit "Bolide" :)

Hatte ich die letzten 6 Jahre lang auch. Trotzdem war -Os bei uns 
bewährter Default. -g3 ist auch hilfreich, mittlerweile werden da sogar 
Makro-Werte in den GDB weiter gereicht.

von 900ss (900ss)


Lesenswert?

Jörg W. schrieb:
> Mein Tipp: lernen, wie man optimierten Code debuggt

Die Hilfen die du beschreibst, kenne ich auch. Aber ich benutze lieber 
-O0 anstatt mich immer mit workarounds rumzuquälen. Dass ich später bei 
-O2 auf die Klappe fliege, hab ich relativ selten erlebt. Die oben 
beschriebene Stelle war die einzige (in meinem Teil) in diesem Projekt.

Es ist auch eine sehr effiziente Logschnittstelle (Spacewire) vorhanden. 
Die schafft ca. 300 Msg/ms. Ja, superschnell und die Daten mit einer Art 
"Komprimierung" versehen. Das geht zu einem PC der das Log dann 
schreibt. Inzwischen wird der Debugger nur noch sehr selten genutzt.

Jörg W. schrieb:
> ein paar GPIOs
Hmmm.... ja. Ich sitze in Norddeutschland und das Target steht in 
Süddeutschland. Da fehlt dafür die Videoübertragung ;)
Als Analyzer hab ich jahrelang auch einen PCI-Bus Analyzer genutzt. Da 
hab ich Variablen, States u.s.w. einfach in ein PCI-Target geschrieben 
wo es nicht störte und das passiert dann ja quasi in Echtzeit.  Der 
Analyzer hat brav alles aufgezeichnet bzw. konnte sogar triggern auf 
gewünschte Zustände. Und man kann auch nach das Timing analysieren. Das 
war sehr komfortabel. Die Kollegen haben sich immer gwundert, dass ich 
beharrlich darauf bestand, die Analyzer auf meinem Tisch zu haben :)

von Walter T. (nicolas)


Lesenswert?

Rolf M. schrieb:
> ilt das nicht an sich für jeden Wert, den man direkt (ohne Berechnung)
> hinein geschrieben hat?

Nein, schon allein weil ein Verlgeich auf nan immer false ergibt, also 
nan != nan.

von Rainer V. (a_zip)


Lesenswert?

Rolf M. schrieb:
> Und ich
> würde davon ausgehen, dass das nicht nur für 123.456, sondern für jeden
> Wert gilt. Die Frage ist eben nur, ob das garantiert ist.

Als "Nichthochsprachler" hat mich besonders dieses Ansinnen doch sehr 
verwundert. Nach meinem bescheidenen Verständnis kann ich "x" doch jeden 
beliebigen, zulässigen Wert (im Sinne von Wert ist möglich und im 
Wertevorrat der Sprache vorhanden) zuweisen und dann auch prüfen, ob x 
gleich diesem Wert ist, der Vergleich also ein "True" liefert. Sollte es 
also Sprachen geben, wo das nicht so ist?? Oder hat der Rolf nur 
"Paranoia"...
Gruß Rainer

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rainer V. schrieb:
> Sollte es also Sprachen geben, wo das nicht so ist?

Das hat nichts mit Sprache zu tun, sondern mit Zahlendarstellung.

In C könnte man mittlerweile sogar exakte (garantierbare) 
Gleitkommawerte festlegen, allerdings in einer menschlich nicht lesbaren 
Form. ;-)

Rate beispielsweise mal, was hier als Zahl ausgegeben wird. :-)
1
#include <stdio.h>
2
3
int
4
main(void)
5
{
6
        printf("%f\n", 0X1.921FB4D12D84AP+1);
7
        return 0;
8
}

Aber: hier ist eben auch das exakte Bitmuster der Zahl garantiert.

von Rainer V. (a_zip)


Lesenswert?

Jörg W. schrieb:
> Rate beispielsweise mal, was hier als Zahl ausgegeben wird. :-)

Jörg W. schrieb:
> 0X1.921FB4D12D84AP+1

Na raten ist ja Quatsch :-) Ich habe aber nach was anderem gefragt, 
nachdem der Jörg eben Sorge hat, dass die Zuweisung und der Test auf 
Zuweisung "False" liefern könnte! Für deine ominöse Zahl gilt also auf 
jeden Fall:
x = 0X1.921FB4D12D84AP+1 und if (x == 0X1.921FB4D12D84AP+1) = "True". 
Ich habe ja gerade vorausgesetzt, dass eine zulässige Zahlendarstellung 
vorliegt. Also ist Rolfs Bedenken Blödsinn??
Gruß Rainer

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rainer V. schrieb:
> Ich habe ja gerade vorausgesetzt, dass eine zulässige Zahlendarstellung
> vorliegt.

Das kannst du aber für eine x-beliebige dezimale Gleitkommazahl nicht 
garantieren, denn es kann sein, dass sie intern nicht exakt darstellbar 
ist.

Aus dem Grunde hat man diese hexadezimale Darstellung zusätzlich ins 
Spiel gebracht.

Es wird übrigens 3.141593 ausgegeben. ;-)

von Rainer V. (a_zip)


Lesenswert?

Jörg W. schrieb:
> 3.141593

Ok, Pi wird also "irgendwie" dargestellt...aber wenn meine Zuweisung x = 
a.bc zulässig ist (egal wie halt), dann liefert die if () immer True. 
Ich habe mich - wie gesagt - über die Sorge von Rolf gewundert, dass das 
irgendwo, irgenwie, irgendwann nicht so sein könnte! Also noch mal 
genauer...die Sorge, dass x = a.bc beim Kompilieren "was Anderes" sein 
könnte, als x == a.bc , ist das denkbar?! Ist es das oder verstehe ich 
nur dummes Zeug?
Gruß Rainer

von Rolf M. (rmagnus)


Lesenswert?

Walter T. schrieb:
> Ok, Pi wird also "irgendwie" dargestellt...aber wenn meine Zuweisung x =
> a.bc zulässig ist (egal wie halt), dann liefert die if () immer True.
> Ich habe mich - wie gesagt - über die Sorge von Rolf gewundert, dass das
> irgendwo, irgenwie, irgendwann nicht so sein könnte!

Mich würde es sehr wundern, wenn es nicht so wäre, aber ich schreibe 
meinen Code ungern basierend auf irgendwelchen Annahmen, von denen ich 
nicht weiß, ob sie stimmen.
Letztendlich hatte Jörg ja geschrieben, dass das nur für 0.0 gelte, bzw. 
er sich für andere Werte nicht sicher ist.

Walter T. schrieb:
> Rolf M. schrieb:
>> ilt das nicht an sich für jeden Wert, den man direkt (ohne Berechnung)
>> hinein geschrieben hat?
>
> Nein, schon allein weil ein Verlgeich auf nan immer false ergibt, also
> nan != nan.

nan ist natürlich ein Sonderfall, denn da ergibt der Vergleich aus einem 
ganz anderen Grund false. Meine Frage war auch nicht, ob der Vergleich 
immer true zurückliefert, sondern ob es bei zwei Vorkommen des selben 
float-literals dazu kommen kann, dass die zu unterschiedlichen Werten im 
Programm führen.

: Bearbeitet durch User
von Rainer V. (a_zip)


Lesenswert?

Rolf M. schrieb:
> sondern ob es bei zwei Vorkommen des selben
> float-literals dazu kommen kann, dass die zu unterschiedlichen Werten im
> Programm führen.

Ok...habe das jetzt für mich mal dahingehend gelöst, dass beliebig 
genaue Zahlen immer noch nicht gleich, sondern unterschiedlich sind. Da 
muß man natürlich gnadenlos ein "Epsilon" einführen oder die ganze 
Geschichte auf einen Datentyp bringen, der für den fraglichen Fall 
vergleichbar ist. Sorgen hat man...
Gruß Rainer

von Rainer V. (a_zip)


Lesenswert?

Rolf M. schrieb:
> Meine Frage war auch nicht, ob der Vergleich
> immer true zurückliefert, sondern ob es bei zwei Vorkommen des selben
> float-literals dazu kommen kann, dass die zu unterschiedlichen Werten im
> Programm führen.

Im Grunde war das natürlich genau deine Frage..aber wahrscheinlich hast 
du gar nicht verstanden, was du wirklich gefragt hast. Höchstenfalls 
hast du was geahnt...sorry, wenn das eine harte Unterstellung sein 
sollte, ist für mich aber genau so :-)
Rainer

von Joachim B. (jar)


Lesenswert?

Jörg W. schrieb:
> 10^6 s?  Das sind knapp 2 Wochen, und ob's dabei auf die letzte µs
> ankommt?

ich sag ja nicht das es sinnvoll ist, aber zu oft wird unsinniges 
gemacht oder gefordert!

von A. S. (Gast)


Lesenswert?

Rolf M. schrieb:
> sondern ob es bei zwei Vorkommen des selben
> float-literals dazu kommen kann, dass die zu unterschiedlichen Werten im
> Programm führen.

fängt ja schon damit an, dass die meisten dezimal-Werte in float nicht 
repräsentiert werden können UND ein Vergleich in double erfolgt.

Also ähnlich wie int i = 28.1; auch später nicht auf Gleichheit geprüft 
werden kann.

von Rolf M. (rmagnus)


Lesenswert?

A. S. schrieb:
> Rolf M. schrieb:
>> sondern ob es bei zwei Vorkommen des selben
>> float-literals dazu kommen kann, dass die zu unterschiedlichen Werten im
>> Programm führen.
>
> fängt ja schon damit an, dass die meisten dezimal-Werte in float nicht
> repräsentiert werden können UND ein Vergleich in double erfolgt.

Ja, die Konvertierung nach double könnte da dazwischenfunken. Allerdings 
kann man mit dem Suffix F das Literal explizit als float angeben. Dann 
wird der Vergleich zwar in double gemacht, aber wenn zwei floats mit dem 
gleichen Wert nach double konvertiert werden, sollte die Eigenschaft der 
Gleichheit ja bestehen bleiben.
Folgender Code
1
    float f = 1.0000000001;
2
    if (f == 1.0000000001)
3
    {
4
        printf("f: gleich\n");
5
    }
6
7
    float g = 1.0000000001F;
8
9
    if (g == 1.0000000001F)
10
    {
11
        printf("g: gleich\n");
12
    }
13
14
    if (f == g)
15
    {
16
        printf("f==g\n");
17
    }
gibt bei mir aus
1
g: gleich
2
f==g

Wie man sieht, schlägt der erste Vergleich aufgrund der höheren 
Präzision von double fehl.

> Also ähnlich wie int i = 28.1; auch später nicht auf Gleichheit geprüft
> werden kann.

Wenn ich es mit (int)28.1 vergleiche, würde ich aber durchaus Gleichheit 
erwarten.

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
Noch kein Account? Hier anmelden.