Forum: Compiler & IDEs C - GCC Optimierung ändert float-multiplikation auf integer mult


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Stefan M. (stefan_m833)


Lesenswert?

Hallo,
eine simple Funktion auf Eclipse  DAVE4  Infineon XMC  ARM4 Core  
GCC
1
static int16_t PhysicalToDigits(float physical, float factor)
2
{
3
   return (int16_t)(physical * factor);
4
}
funktioniert wie erwartet im Debug Mode. Sobald die Optimierung -O oder 
-O1 eingestellt wird, so wird das Ergebnis je nach Aufrufstelle 
manchmal geändert, und zwar demhingehend, dass beide Multiplikanten 
vmtl. erst uint16_t gecastet werden und dann erst multipliziert. Konkret 
ist das Ergebnis physical=3.05f factor=16.5f ohne Optimierung 50 und mit 
der Optimierung 48 (!). Offensichtlich wurde mindestens aus "factor" 
eine 16. Je nach Aufruf optimiert der Compiler das ganz simpel 
entsprechend des assemblercodes zu einem 4-fach shift (klingt ja 
praktisch).

Frage: Was habe ich falsch gemacht/eingestellt, wieso optimiert der 
Compiler hier ungefragt und ändert das Ergebnis durchaus derart 
drastisch? Sollte ich ein C-Buch kaufen oder einen anderen Compiler^^?

Danke für Tipps!

von Oliver S. (oliverso)


Lesenswert?

Stefan M. schrieb:
> Frage: Was habe ich falsch gemacht/eingestellt, wieso optimiert der
> Compiler hier ungefragt und ändert das Ergebnis durchaus derart
> drastisch?

Das wird doch eher der übliche Fehler in Zeile 42 sein.

Oliver

von Stefan M. (stefan_m833)


Lesenswert?

Ok, danke für den Hinweis auf die fehlende Auflistung der Einstellungen. 
Es ist so, dass das Problem, soweit ich weiß*, auch ohne jede Änderung 
der "Defaults" auftritt (bis auf -O1). Das bringt vmtl hier nicht 
weiter, daher lassen Sie mich es anders Formulieren:

- Möchte man so ein Problem in Zukunft umgehen, welche Einstellungen im 
Compiler oder Code-Guidelines wäre denn zu beachten?

Der Hintergrund ist: Stelle ich die Funktion by luck um, schreibe etwas 
volatile oder rechne halt durchgehend in Float, so ist das Problem 
gelöst. Darum geht's mir für einen Einzelfall gar nicht, mich würde viel 
eher interessieren, wie ich sowas in Zukunft verhindern kann - Also was 
könnte hier falsch gelaufen sein? Was kann ich daraus lernen? Wie kann 
ich anhand diesen Beispiels C-Code so schreibe, dass rauskommt, was ich 
will? ;) Guidelines zu Casting, zu C-Funktionen und z.B. zu 
Compilereinstellungen Richtung fast-Math oder Hardware-float Optionen 
haben keine Auskunft über derartige Fehler gebracht.

*Wie man sieht, habe ich nur die Funktion geschrieben, jemand anderes 
hat diese verwendet. Compilereinstellungen kann/werde ich ab nächster 
Woche noch nachliefern. (sorry dass ichs nicht direkt griffbereit habe) 
danke soweit!

von Hans W. (Firma: Wilhelm.Consulting) (hans-)


Lesenswert?

Compiliere mal mit -Wextra und -Wall und schau dir die warnings an.

im C++-Modus ist meiner Erfahrung nach die Anzahl der Warnings und deren 
"Qualität" besser... kann aber auch nur Zufall und Einbildung sein.

Normalerweise sagt dir dein Compiler, wenn er etwas tut, bei dem er 
vermutet, dass es das Ergebnis beeinflusst.

73

von MaWin (Gast)


Lesenswert?

Also ich tippe ja sehr selten auf "Compilerbug", weil es zu 99% kein 
Compilerbug ist, sondern ein Anwenderhirnbug.
Aber in diesem Fall, wenn das tatsächlich die verwendete Funktion und 
nicht eine neu fürs Forum eingetippte Version ist, dann würde ich 
tatsächlich auf Compilerbug tippen.
Anscheinend wird der Cast vor der Multiplikation durchgeführt.

Also: Ist das wirklich die Funktion, oder hast du sie für das Forum neu 
getippt und dabei evtl. unbeabsichtigt leicht verändert?

von Oliver S. (oliverso)


Lesenswert?

MaWin schrieb:
> Aber in diesem Fall, wenn das tatsächlich die verwendete Funktion und
> nicht eine neu fürs Forum eingetippte Version ist, dann würde ich
> tatsächlich auf Compilerbug tippen.

Ganz ehrlich? Bei Fehlern bei solchen Basics muß der dann aber doch 
schon arg kaputt sein.

Oliver

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


Lesenswert?

Stefan M. schrieb:
> Sollte ich ein C-Buch kaufen oder einen anderen Compiler^^?

Im Zweifel immer ersteres. ;-)

Zeig doch mal die Codestellen, wo er 48 einsetzt.

von Markus F. (mfro)


Lesenswert?

Compileroptionen?

Dir ist bewusst, dass die M4 FPU nur single precision (float) kann?

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


Lesenswert?

Markus F. schrieb:
> Dir ist bewusst, dass die M4 FPU nur single precision (float) kann?

Hat er ja auch benutzt.

von MaWin (Gast)


Lesenswert?

Ein Disassembly der mutmaßlich fehlcompilierten Funktion (mit 
Optimierung an) wäre jedenfalls sehr hilfreich zur Beurteilung der Lage.

objdump --disassemble file.o

Oliver S. schrieb:
> Ganz ehrlich? Bei Fehlern bei solchen Basics muß der dann aber doch
> schon arg kaputt sein.

Compilerbugs treten oft, wenn sie denn auftreten, auch gerne einmal in 
recht einfach erscheinenden Situationen auf.

Und ja, ich bin da auch skeptisch. So gut wie immer ist es stattdessen 
ein Programmierfehler.
Aber aufgrund der hier gegebenen Informationen sehe ich keine andere 
Möglichkeit als einen Compilerbug oder halt, wie bereits gesagt, 
fehlende Informationen oder leicht anderer Code.

Markus F. schrieb:
> Dir ist bewusst, dass die M4 FPU nur single precision (float) kann?

Wie kann das Ergebnis 48 bei single precision bei dieser Eingabe 
(physical=3.05f factor=16.5f) vorkommen?

von Markus F. (mfro)


Lesenswert?

Jörg W. schrieb:
> Markus F. schrieb:
>> Dir ist bewusst, dass die M4 FPU nur single precision (float) kann?
>
> Hat er ja auch benutzt.

Schon klar. Aber was passiert, wenn man die single precision FPU mit 
Binaries füttert, die mit den falschen Compileroptionen erzeugt wurden?
Ich jedenfalls habe die (verwirrlich vielen) möglichen Optionen noch 
nicht in sämtlichen (falschen) Kombinationen durchprobiert

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Markus F. schrieb:
> Jörg W. schrieb:
>> Markus F. schrieb:
>>> Dir ist bewusst, dass die M4 FPU nur single precision (float) kann?
>>
>> Hat er ja auch benutzt.
>
> Schon klar. Aber was passiert, wenn man die single precision FPU mit
> Binaries füttert, die mit den falschen Compileroptionen erzeugt wurden?

Alles, aber bestimmt kein Shift an Stelle der float-Multiplikation.

Oliver

von Stefan M. (stefan_m833)


Lesenswert?

Hallo, sehr vielen Dank an Alle für die ersten Tipps und Nachfragen. 
Leider ist das ganze Projekt mit RTOS, Tasks, Tokens und verschiedensten 
Aufrufen so umfangreich, dass ich das natürlich auf das Problem 
"herunterkürzen" muss :/

Aber bei der vielen Antworten hier setze ich mich ab morgen früh nochmal 
hin und poste den ganzen Funktionscode und die Aufrufe, also solche in 
denen es funktioniert und bei denen es hakt.
Auch die Warnings schaue ich mit den empfohlenen Options nochmal durch 
und schau mal was sich mit dem disassembly in der Umgebung machen lässt.

Ja, erfahrungsgemäß liegt das Problem immer woanders, wo mans nicht 
vermutet. Den C-Code irgendwo zwischen Compiler, Anwender und Ergebnis 
(auf dem CAN-Bus) anzusehen halte ich für ein guten Schuss, 
nachzuforschen was los ist ;)

EDIT: Hier der original Code, unverändert, 100% copy,
1
static int16_t PhysicalToDigits(float physical, float factor, float offset)
2
{
3
  return (int16_t)( (physical + offset) * factor );
4
}
aber auch andere Varianten mit Klammern, Zwischenwerten, afaik auch wie 
oben gezeigt ohne Summand, zeigtes das gleiche Problem (Varianten kann 
ich auch schicken bei Bedarf)

Und ein Beispielaufruf, der funktioniert (daher halte ich den Summanden 
nicht für interessant - er sorgt dafür, dass der Code sich ändert und 
dass es geht):
1
void FPGA_SetLimitsU_OUT(float U_OUT_max, float U_OUT_min){
2
  Send_SPItoFPGA(WRITE_U_OUT_MAX, PhysicalToDigits(U_OUT_max, 16.5f, CAL_U_OUT_OFFSET));
3
  Send_SPItoFPGA(WRITE_U_OUT_MIN, PhysicalToUnsignedDigits(U_OUT_min, CAL_U_OUT_FACTOR, CAL_U_OUT_OFFSET));
4
}
mit CAL_U_OUT_FACTOR und CAL_U_OUT_OFFSET als defines -> float 
konstanten -> genaue Prüfung aber nochmal morgen.

Und hier das Problem: Hier versagt der Aufruf bzw. die Rückgabe:
1
void FPGA_SetLimitsI_OUT(float I_OUT_max){
2
  Send_SPItoFPGA(WRITE_I_OUT_MAX, PhysicalToDigits(I_OUT_max, 16.5f, 0.0f));
3
}
Bei Optimierung verschwindet der Aufruf an der Stelle und es ist 
schwierig hier den Rückgabewert zu "identifizieren".

Aber nochmal konkret: Alleine das Umstellen von ohne Optimierung auf 
Optimierung bringt statt 50 (Ampere) hartnäckig 48A auf einem 
CAN-Interface. Und auch im Assemblercode ist zu sehen / soll zu sehen 
sein (liegt mir leider gerade nicht vor, -> morgen), dass statt der 
floatoperations nun ein shift drinn steht. Von der Funktion bis zum CAN 
ist es ein weiter Weg, ich weiß - ich kann also nur sagen, dass es auf 
die genannten stellen bisher herunterdebugged wurde und ich aktuell 
davon ausgehe, dass die Funktion Usache ist. -> mehr morgen :)

Guten Abend und danke!

von Oliver S. (oliverso)


Lesenswert?

Stefan M. schrieb:
> Bei Optimierung verschwindet der Aufruf an der Stelle und es ist
> schwierig hier den Rückgabewert zu "identifizieren".

Was zu erwarten ist. Der Compiler berechnet das Ergebnis schon zur 
Compilezeit, und im Compilat findet sich nur noch das Ergebnis.

Oliver

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Stefan M. schrieb:
> static int16_t PhysicalToDigits(float physical, float factor, float
> offset)
Trenne doch als Test mal die Berechnung und die Rückgabe als int.
Was sagt dann der debuger?
1
static int16_t PhysicalToDigits(float physical, float factor, float 
2
offset)
3
{
4
  float x = (physical + offset) * factor;
5
  return x;
6
}

dito mit deinem Aufruf einer Funktion inhalt eines anderen 
Funktionsaufrufes:
1
float x = PhysicalToDigits(U_OUT_max, 16.5f, CAL_U_OUT_OFFSET);
2
Send_SPItoFPGA(WRITE_U_OUT_MAX, x);

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


Lesenswert?

Irgend W. schrieb:
> Was sagt dann der debuger?

Den kannst du an der Stelle vergessen. Der Compiler optimiert ja die 
Aufrufe der (statischen) Funktionen in zur Compilezeit berechnete 
Konstanten um.

von MaWin (Gast)


Lesenswert?

Stefan M. schrieb:
> Bei Optimierung verschwindet der Aufruf an der Stelle und es ist
> schwierig hier den Rückgabewert zu "identifizieren".

Ok.
Was passiert, wenn du das static entfernst?
Ist der Fehler dann immer noch da?
Wird dann die Funktion nicht wegoptimiert?
Wenn ja+ja, dann bitte disassembly.

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


Lesenswert?

MaWin schrieb:
> Was passiert, wenn du das static entfernst?

Dann wird der Compiler sie nicht mehr inlinen wollen.

Ist aber nur ein Würgaround, keine echte Lösung.

von Markus F. (mfro)


Lesenswert?

mein arm-none-eabi-gcc (8.3.1) macht aus dem hier:
1
static int16_t PhysicalToDigits(float physical, float factor)
2
{
3
   return (int16_t)(physical * factor);
4
}
5
6
...
7
PhysicalToDigits((float) 3.05f, (float) 16.5f);

schlicht
1
        mov     r0, #50

von MaWin (Gast)


Lesenswert?

Jörg W. schrieb:
> Ist aber nur ein Würgaround, keine echte Lösung.

Eine Lösung war nicht das Ziel. Beiträge bitte vor dem Antworten lesen.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Markus F. schrieb:
> schlicht
>         mov     r0, #50

Das passiert, wenn der compiler schlauer ist, als der, der ihn aufruft 
;-)

scnr,
WK

von Stefan M. (stefan_m833)


Lesenswert?

Hallo zusammen,

- Wall und Wextra bringen keine Fehler in unserem Code (Nur 140 Fehler 
in den Dave-Apps und XMC Libs)

"Ok.
Was passiert, wenn du das static entfernst?" - bleibt identisch

also es ist so wie folgt:
Ohne Optimierung (alles funktioniert) ist der Aufruf der Funktion zu 
sehen (branch to 8004914)
1
void FPGA_SetLimitsI_OUT(float I_OUT_max){
2
 8004b10:  b580        push  {r7, lr}
3
 8004b12:  b082        sub  sp, #8
4
 8004b14:  af00        add  r7, sp, #0
5
 8004b16:  6078        str  r0, [r7, #4]
6
  Send_SPItoFPGA(WRITE_I_OUT_MAX, PhysicalToDigits(I_OUT_max, CAL_I_OUT_FACTOR, 0));
7
 8004b18:  6878        ldr  r0, [r7, #4]
8
 8004b1a:  4907        ldr  r1, [pc, #28]  ; (8004b38 <FPGA_SetLimitsI_OUT+0x28>)
9
 8004b1c:  f04f 0200   mov.w  r2, #0
10
 8004b20:  f7ff fef8   bl  8004914 <PhysicalToDigits>
11
 8004b24:  4603        mov  r3, r0
12
 8004b26:  b29b        uxth  r3, r3
13
 8004b28:  f24b 3002   movw  r0, #45826  ; 0xb302
14
 8004b2c:  4619        mov  r1, r3
15
 8004b2e:  f000 f965   bl  8004dfc <Send_SPItoFPGA>
16
}
An der Stelle 8004914 sieht man die Funktionen incl. aller nötigen 
float-ops:
1
08004914 <PhysicalToDigits>:
2
#include "FPGA_interface.h"
3
#include "config.h"
4
static int16_t PhysicalToDigits(float physical, float factor, float offset){
5
 8004914:  b480        push  {r7}
6
 8004916:  b085        sub  sp, #20
7
 8004918:  af00        add  r7, sp, #0
8
 800491a:  60f8        str  r0, [r7, #12]
9
 800491c:  60b9        str  r1, [r7, #8]
10
 800491e:  607a        str  r2, [r7, #4]
11
  return (int16_t)((physical + offset) * factor);
12
 8004920:  ed97 7a03   vldr  s14, [r7, #12]
13
 8004924:  edd7 7a01   vldr  s15, [r7, #4]
14
 8004928:  ee37 7a27   vadd.f32  s14, s14, s15
15
 800492c:  edd7 7a02   vldr  s15, [r7, #8]
16
 8004930:  ee67 7a27   vmul.f32  s15, s14, s15
17
 8004934:  eefd 7ae7   vcvt.s32.f32  s15, s15
18
 8004938:  edc7 7a00   vstr  s15, [r7]
19
 800493c:  883b        ldrh  r3, [r7, #0]
20
 800493e:  b29b        uxth  r3, r3
21
 8004940:  b21b        sxth  r3, r3
22
}


Nun zurück zum Problem: alles identisch, jedoch nur auf -O1 umgestellt:
1
void FPGA_SetLimitsI_OUT(float I_OUT_max){
2
 800295c:  b500        push  {lr}
3
 800295e:  b083        sub  sp, #12
4
#include "config.h"
5
6
static int16_t PhysicalToDigits(float physical, float factor, float offset){
7
  return (int16_t)((physical + offset) * factor);
8
 8002960:  eddf 7a09   vldr  s15, [pc, #36]  ; 8002988 <FPGA_SetLimitsI_OUT+0x2c>
9
 8002964:  ee07 0a10   vmov  s14, r0
10
 8002968:  ee77 7a27   vadd.f32  s15, s14, s15
11
 800296c:  eefe 7acf   vcvt.s32.f32  s15, s15, #2
12
}
Der Aufruf wird weggelassen (ist ja soweit ok, inling) und der ganze 
Spaß durch VCVT ersetzt.

Ist das die Info die Ihr/Ich braucht/e um dem Problem auf die Spur zu 
kommen?

Ich war in der Annahme, dass der dritte Parameter eine Shiftanweisung 
ist, aber das ist mir nun auf Basis von 
https://developer.arm.com/documentation/dui0473/m/vfp-instructions/vcvt--between-floating-point-and-integer- 
und https://booksite.elsevier.com/9780124080829/downloads/APP-01.pdf 
doch unklar. Hat jemand einen Tipp, wieso es bei O1 auf VCVT setzt, was 
es genau bedeutet, warum es mathematisch fehlschlägt?
Wie immer vielen Dank

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


Lesenswert?

Interessant wäre zumindest noch, was auf 8002988 für eine Konstante 
steht. Die wird ja anfangs nach r15 geladen.

Ich denke trotzdem, dass das so einfach nicht zu analysieren ist, weil 
da ziemlich viele Dinge reinspielen, die du hier nicht zitiert hast. 
Insbesondere, welche der von dir benutzten Symbole alle schon zur 
Compilezeit bekannt sind, spielt hier eine große Rolle, denn das sind 
die Stellen, an denen der Compiler halt optimieren kann.

Ganz blicke ich bei der Doku für VCVT auch nicht durch. Die #2 scheint 
die Anzahl der fraction bits zu sein, was ja letztlich einer 
Schiebeoperation nahe kommt.

von Stefan M. (stefan_m833)


Lesenswert?

Jörg W. schrieb:
> Interessant wäre zumindest noch, was auf 8002988 für eine
> Konstante
> steht. Die wird ja anfangs nach r15 geladen.
>
> Ich denke trotzdem, dass das so einfach nicht zu analysieren ist, weil
> da ziemlich viele Dinge reinspielen, die du hier nicht zitiert hast.
> Insbesondere, welche der von dir benutzten Symbole alle schon zur
> Compilezeit bekannt sind, spielt hier eine große Rolle, denn das sind
> die Stellen, an denen der Compiler halt optimieren kann.

Ok, stimmt, ich versuche die Infos aufzulisten:

Der genaue betreffende Aufruf ist im c code wie folgt:
1
void FPGA_SetLimitsI_OUT(float I_OUT_max){
2
  Send_SPItoFPGA(WRITE_I_OUT_MAX, PhysicalToDigits(I_OUT_max, CAL_I_OUT_FACTOR, 0));
3
}
also wie schon ersichtlich ist I_OUT_max erst zur Laufzeit bekannt, die 
anderen schon vorher:

- dritte Parameter "0" ist 0
- zweite Parameter ist ein define:
1
#define CAL_I_OUT_FACTOR 4.16//Digits per Ampere
- der erste Parameter ist ein float und kommt von
1
 FPGA_SetLimitsI_OUT(controlLimits.I_OUT_max);

und wird instantiiert durch
1
 limits_t controlLimits;
das struct sieht wiederum so aus:
1
typedef struct {
2
  float U_IN_max;
3
  float U_IN_min;
4
  float U_OUT_max;
5
  float U_OUT_min;
6
  float U_ZK_max;
7
  float U_ZK_min;
8
  float IPh_max;
9
  float I_OUT_max;
10
} limits_t;
etc.. die Zuweisungen ziehen sich durch noch mehrere Ebenen. Aber selbst 
falls I_Out_max mal mit einem int oder sonstigem zugewisen werden 
sollte, erklärt es mir noch nicht, warum ausgerechnet die harte 
definition von 4.16 zu einem 2-fach shift wegignoriert wird :( ?

Wie ihr seht, ist das eine zweite Stelle, an der das fehlschlägt 
(identisch zu dem oberen 16.5f Beispiel)
-> Mitstreiter haben empirisch herausgefunden, dass die Optimierung 
immer fehlschlägt, wenn der Multiplikator eben nahe eines 2^n wertes 
liegt. Ansonsten bleibts korrekt bei float, also andere Werte werden auf 
Basis einer kleinen nicht dokumentierten Versuchsrunde nicht durch den 
Shift wegoptimiert (!)

Was sollte/könnte ich noch an Details schicken?

von Εrnst B. (ernst)


Lesenswert?

Stefan M. schrieb:
> Was sollte/könnte ich noch an Details schicken?

Am einfachsten:
Poste deinen Code-Schnippsel auf
https://godbolt.org/, diverse ARM-GCCs sind dort verfügbar.
Wenn er dort compiliert und den Fehler zeigt, stell den Link hier rein. 
Dann kann jeder daran rumdrehen und verschiedene Theorien austesten...

von Stefan M. (stefan_m833)


Lesenswert?

Εrnst B. schrieb:
> Am einfachsten:
> Poste deinen Code-Schnippsel auf
> https://godbolt.org/, diverse ARM-GCCs sind dort verfügbar.
> Wenn er dort compiliert und den Fehler zeigt, stell den Link hier rein.
> Dann kann jeder daran rumdrehen und verschiedene Theorien austesten...

ein compiler für den M4 ARM scheint mir dort nicht auswählbar :/

Aber immerhin habe ich den Compiler von Version 4.9 auf 10.3 mal 
geupdated. Leider ist auch der 6 Jahre neuere Compiler immernoch 
gleicher oder ähnlicher Meinung. Übrigens @ Jörg W: Zeile
8002988:  00000000   .word  0x00000000
was mir so dermaßen nutzlos ist/erscheint (wozu 0 laden und auch noch 
float addieren?) dass ich mich wirklich Frage what the hell hier falsch 
läuft. Stuxnet?

Hier übrigens noch die ganzen Flags:

-O1 -ffunction-sections -fdata-sections -Wall -Wextra -std=gnu99 
-mfloat-abi=softfp -Wa,-adhlns="$@.lst" -pipe -c -fmessage-length=0 
-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mthumb -g -gdwarf-2

von Nop (Gast)


Lesenswert?

Stefan M. schrieb:
> -std=gnu99

Versuch's mal mit -std=c99.

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


Lesenswert?

Stefan M. schrieb:

> Übrigens @ Jörg W: Zeile
> 8002988:  00000000   .word  0x00000000
> was mir so dermaßen nutzlos ist/erscheint (wozu 0 laden und auch noch
> float addieren?)

Du addierst den ersten Parameter (r0). Ich denke, die Addition mit 0.0f 
macht dann eine Float-Zahl draus. Aber so sehr sattelfest bin ich da 
auch nicht, ich würde mich mit dem Debugger durch hangeln.

> -O1 -ffunction-sections -fdata-sections -Wall -Wextra -std=gnu99
> -mfloat-abi=softfp

Warum eigentlich softfp, wenn du eine FPU hast?

von Stefan M. (stefan_m833)


Lesenswert?

Jörg W. schrieb:
> Ich denke trotzdem, dass das so einfach nicht zu analysieren ist, weil
> da ziemlich viele Dinge reinspielen, die du hier nicht zitiert hast.
> Insbesondere, welche der von dir benutzten Symbole alle schon zur
> Compilezeit bekannt sind, spielt hier eine große Rolle, denn das sind
> die Stellen, an denen der Compiler halt optimieren kann.

Ok, ich bin dem Nachgegangen. Ändere ich
1
#define CAL_I_OUT_FACTOR 4.16//Digits per Ampere //
zu
1
#define CAL_I_OUT_FACTOR 4.16//Digits per Ampere //
2
#define CAL_I_OUT_FACTOR2 4.16//Digits per Ampere //

sowie
1
void FPGA_SetLimitsI_OUT(float I_OUT_max){
2
  Send_SPItoFPGA(WRITE_I_OUT_MAX, PhysicalToDigits(I_OUT_max, CAL_I_OUT_FACTOR, 0));
3
}
 zu
1
void FPGA_SetLimitsI_OUT(float I_OUT_max){
2
  Send_SPItoFPGA(WRITE_I_OUT_MAX, PhysicalToDigits(I_OUT_max, CAL_I_OUT_FACTOR, 0));
3
}
, so ist es gefixt:
1
void FPGA_SetLimitsI_OUT(float I_OUT_max){
2
 80029d0:  b508        push  {r3, lr}
3
 80029d2:  ee07 0a90   vmov  s15, r0
4
  return (int16_t)((physical + offset) * factor);
5
 80029d6:  ed9f 7a09   vldr  s14, [pc, #36]  ; 80029fc <FPGA_SetLimitsI_OUT+0x2c>
6
 80029da:  ee77 7a87   vadd.f32  s15, s15, s14
7
 80029de:  ed9f 7a08   vldr  s14, [pc, #32]  ; 8002a00 <FPGA_SetLimitsI_OUT+0x30>
8
 80029e2:  ee67 7a87   vmul.f32  s15, s15, s14
9
 80029e6:  eefd 7ae7   vcvt.s32.f32  s15, s15
10
  Send_SPItoFPGA(WRITE_I_OUT_MAX, PhysicalToDigits(I_OUT_max, CAL_I_OUT_FACTOR2, 0));
11
 80029ea:  ee17 3a90   vmov  r3, s15
12
 80029ee:  b299        uxth  r1, r3
13
 80029f0:  f24b 3002   movw  r0, #45826  ; 0xb302
14
 80029f4:  f7ff ff3c   bl  8002870 <Send_SPItoFPGA>
15
}
Das Problem liegt also "woanders", also beim nutzen des #defines ? Wenn 
ich das gesamt GIT-Repo nach "CAL_I_OUT_FACTOR" finde ich nur eben genau 
diese eine einzige Stelle, wo es definiert ist. Genutzt wird es an genau 
3 Stellen, dem genannten Code, sowie hier: (praktisch die 
Kehrfunktionen)
1
digits = Get_FPGA_Data(READ_I_OUT);
2
physical = DigitsToPhysical(digits, CAL_I_OUT_FACTOR, 0);
 und
1
digits = Get_FPGA_Data(READ_UnR_0);
2
physical = DigitsToPhysical(digits, CAL_I_OUT_FACTOR, 0);
 und

Was könnte die Ursache sein, dass nur bei genau dem Define-Wording der 
Compiler der Meinung ist, bei -O1 daraus eine 4 machen zu wollen? Oder 
ist es eben einfach Zufall, man ändert hier etwas und ausversehen passt 
es gerade wieder ?

von Stefan M. (stefan_m833)


Lesenswert?

Jörg W. schrieb:
> Warum eigentlich softfp, wenn du eine FPU hast?

weil ich die IDE gerade ganz frisch aufgesetzt habe und keinen Parameter 
ändern wollte. Anscheinend greift er so aber auch von gerne alleine nach 
der hwfpu, ansonsten hab ichs aber tatächlich immer auf -mfloat-abi=hard

von Stefan M. (stefan_m833)


Lesenswert?

Nop schrieb:
> Stefan M. schrieb:
>> -std=gnu99
>
> Versuch's mal mit -std=c99.

geht leider nicht, endet in
1
error: Scania_150kW.elf uses VFP register arguments, c:/program files (x86)/gnu arm embedded toolchain/10 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m+fp/softfp\libg_nano.a(lib_a-closer.o) does not

von Stefan M. (stefan_m833)


Lesenswert?

Stefan M. schrieb:

zu
> void FPGA_SetLimitsI_OUT(float I_OUT_max){
>   Send_SPItoFPGA(WRITE_I_OUT_MAX, PhysicalToDigits(I_OUT_max,
> CAL_I_OUT_FACTOR, 0));
> }, so ist es gefixt:

heißt natürlich zu
1
void FPGA_SetLimitsI_OUT(float I_OUT_max){
2
  Send_SPItoFPGA(WRITE_I_OUT_MAX, PhysicalToDigits(I_OUT_max, 
3
CAL_I_OUT_FACTOR2, 0));
4
}

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Stefan M. schrieb:
> #define CAL_I_OUT_FACTOR 4.16//Digits per Ampere //

Da haette ich aber schon alleine wegen des dusseligen c++ Kommentar 
hinter dem Macro uebelste Bauchschmerzen.

Gruss
WK

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


Lesenswert?

Stefan M. schrieb:
> Was könnte die Ursache sein, dass nur bei genau dem Define-Wording der
> Compiler der Meinung ist, bei -O1 daraus eine 4 machen zu wollen?

Dass dein #define irgendwo anders überbügelt wird.

Schau dir mal die Ausgabe des Präprozessors an. Die bekommst du, indem 
du in der Compiler-Kommandozeile das -c durch ein -E ersetzt (und dann 
natürlich sinnvollerweise hinter -o einen anderen Dateinamen angibst). 
Am besten fügst du auch noch die Option -dM hinzu, das dumpt dir die 
finalen Werte aller mittels #define generierten Makros am Ende.

Stefan M. schrieb:
> Scania_150kW.elf uses VFP register arguments, c:/program files (x86)/gnu
> arm embedded toolchain/10
> 2021.10/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/li 
b/thumb/v7e-m+fp/softfp\libg_nano.a

Klar, da steht ja in der Angabe der libg_nano auch ein "softfp" drin. Du 
müsstest dort natürlich schon auch die hardfp-Variante auswählen.

Aber bleib erstmal bei einer Baustelle. ;-)

von Stefan M. (stefan_m833)


Lesenswert?

Dergute W. schrieb:
> Moin,
>
> Stefan M. schrieb:
>> #define CAL_I_OUT_FACTOR 4.16//Digits per Ampere //
>
> Da haette ich aber schon alleine wegen des dusseligen c++ Kommentar
> hinter dem Macro uebelste Bauchschmerzen.
>
> Gruss
> WK

Danke - auch eine Lösung. Ich halte fest:

Alle 4 Stellen im Repo von CAL_I_OUT_FACTOR auf CAL_I_OUT_FACTOR2 
ersetzen geht.
1
 #define CAL_I_OUT_FACTOR 4.16//Digits per Ampere //
durch
1
#define CAL_I_OUT_FACTOR 4.16 //Digits per Ampere //
 ersetzen geht auch

Ist das eben Zufall und bei nächster Gelegenheit habe ich das gleiche 
Problem wieder? Oder ist die Antwort "keine dusseligen c++ Kommentar ins 
Define weil -O1 das nicht mag?/ wenigestens ein Leerzeichen dazwischen?

Ist "//" ein geheime O1 Anweisung das Define nicht so genau zu nehmen?

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Stefan M. schrieb:
> - zweite Parameter ist ein define:#define CAL_I_OUT_FACTOR 4.16//Digits
> per Ampere

das ist ein double. Scheint aber nicht das Problem zu sein.

von Stefan M. (stefan_m833)


Lesenswert?

Jörg W. schrieb:

> Dass dein #define irgendwo anders überbügelt wird.

Kann es aber nicht finden :/. Irgendwie evtl außerhalb des Repos in 
einer Bib oder sonstiges. (Google kennt den Ausdruck soweit nicht, also 
wohl keine Doppelbelegung anderswo)
Auch weiterhin seltsamst der Unterschied O0 und O1. :/

von foobar (Gast)


Lesenswert?

Ich vermute mal unterschiedliche Rounding-Modes zwischen Compile- und 
Run-Time.  Insb wenn die CPU kein double kann, gibt's da krasse 
Unterschiede.  Gibt beim GCC ne Masse an Switches (-ffast-math, 
-frounding-math, ...).

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


Lesenswert?

Stefan M. schrieb:
> Kann es aber nicht finden :/.

Daher meine Empfehlung, sich die Ausgabe des Präprozessors anzusehen.

Das hat mir schon oft beim Debuggen seltsamer Effekte geholfen.

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


Lesenswert?

Dergute W. schrieb:
> Da haette ich aber schon alleine wegen des dusseligen c++ Kommentar
> hinter dem Macro uebelste Bauchschmerzen.

Der ist hässlich, keine Frage. :-) Allerdings werden Kommentare bereits 
vom Präprozessor durch Leerzeichen ersetzt, insofern ist das hier nicht 
das Problem.

von Stefan M. (stefan_m833)


Lesenswert?

Hallo,
leider funktioniert es jetzt. Per GIT kann ich sehen, dass der 
identische C-Code nun funktioniert. Es funktioniert nun zuverlässig, 
seitdem ich an den Kommentaren geschraubt habe und auf Rebuild Project 
gegangen bin. Ich muss wohl noch etwas an der Umgebung geändert haben, 
kann es aber nicht mehr zurückstellen. Eigentlich ist nur noch der große 
Unterschied, dass ich den neuen Compiler verwende, der aber heute 
Vormittag noch falsche Ergebnisse brachte. Evtl ein Cache-Problem von 
Neu auf Alt? Habe ich wirklich seit dem nichtmehr auf full-rebuild 
geklickt? Hat ein Eclipse-Restart zum Mittagessen was geändert?

Ich sichere jetzt mal den Zustand soweit es geht und setze alles wieder 
neu auf um den Fehler überhaupt wieder zu bekommen. Aber evtl erst ab 
morgen wieder. Nicht zu wissen was los war ist noch schlechter als der 
Zustand in Post #1.

EDIT:
Ok, hab alles neu installiert.
Es ist nun so: Frischer Repo-Pull, frische DAVE Installation, NICHTS 
ändern (ok, auf optimize auf O1 stellen), den Compiler entsprechend der 
Anleitung* von
ARM-GCC-49 auf GCC 10-2021.10
ändern führt zur Lösung.
*https://community.infineon.com/t5/DAVE/Upgrading-GCC-in-DAVE4/td-p/310321

*->> Also wohl ein Compilerbug aus 2015.*

Sorry für die letzten Posts über die Kommentare, es war wohl ein 
cache-Problem. Wäre auch zu crazy gewesen. Mal sehen ob das jetzt stabil 
ist und bei den Kollegen auch hilft. Falls das nun die Lösung ist, 
vielen VIELEN vielen Dank für das Mitdenken/Anregen. Ansonsten bis 
bald/gleich wieder **irresmilie**

: Bearbeitet durch User
von MaWin (Gast)


Lesenswert?

Stefan M. schrieb:
> Der Aufruf wird weggelassen (ist ja soweit ok, inling) und der ganze
> Spaß durch VCVT ersetzt.
>
> Ist das die Info die Ihr/Ich braucht/e um dem Problem auf die Spur zu
> kommen?

Das sieht für mich ziemlich eindeutig nach einer Fehlcompilierung aus.

Stefan M. schrieb:
> Hallo,
> leider funktioniert es jetzt. Per GIT kann ich sehen, dass der
> identische C-Code nun funktioniert. Es funktioniert nun zuverlässig,
> seitdem ich an den Kommentaren geschraubt habe und auf Rebuild Project
> gegangen bin. Ich muss wohl noch etwas an der Umgebung geändert haben,
> kann es aber nicht mehr zurückstellen. Eigentlich ist nur noch der große
> Unterschied, dass ich den neuen Compiler verwende, der aber heute
> Vormittag noch falsche Ergebnisse brachte. Evtl ein Cache-Problem von
> Neu auf Alt?

Ok, sehr interessant :)
Das heißt du nutzt nun den neuesten Compiler mit einem sauberen Rebuild 
und es ist weg? Dann würde ich es dabei belassen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Stefan M. schrieb:
> 8002988:  00000000   .word  0x00000000
> was mir so dermaßen nutzlos ist/erscheint (wozu 0 laden und auch noch
> float addieren?) dass ich mich wirklich Frage what the hell hier falsch
> läuft.

Vermutlich ein Dis-Assembly mit denen von dir angegebenen Optionen:

> Hier übrigens noch die ganzen Flags:
>
> -O1 -ffunction-sections -fdata-sections -Wall -Wextra -std=gnu99
> -mfloat-abi=softfp -Wa,-adhlns="$@.lst" -pipe -c -fmessage-length=0
> -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mthumb -g -gdwarf-2

Mit "-c" wird nur ein Object-File erzeugt, das noch RELOCs enthält, also 
nicht aufgelöste Symbole.  RELOCs kann man anzeigen lassen, wenn man 
objdump zusätzlich mit -r aufruft.

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


Lesenswert?

Was dagegen spricht (bzw. dafür, dass er das final gelinkte File 
disassembliert hat) ist, dass die Adressen nicht zu einem relocatable 
object passen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wenn das Listing per

> -Wa,-adhlns="$@.lst"

erstellt wird, ist es ja auch vom Assembler, also nicht gelinkt?

...andererseits sind die Konstanten wohl schon dem Compiler bekannt, 
also keinen Grund für 'nen RELOC.

: Bearbeitet durch User
von Stefan M. (stefan_m833)


Lesenswert?

MaWin schrieb:
> Das sieht für mich ziemlich eindeutig nach einer Fehlcompilierung aus.

Ja, sieht nun danach aus, dein Tip scheint so zu stimmen. Also ich habe 
es nun auch nochmal auf einem komplett fremden System exerziert. Hier 
praktisch nochmal für die Nachwelt zusammengestellt:

-> Neueste Version von DAVE (4.5.0), Frisches Repo. Output:
1
0800295c <FPGA_SetLimitsI_OUT>:
2
void FPGA_SetLimitsI_IN(float I_IN_max){
3
  Send_SPItoFPGA(WRITE_I_IN_MAX, PhysicalToDigits(I_IN_max, CAL_I_IN_FACTOR, 0));
4
}
5
void FPGA_SetLimitsI_OUT(float I_OUT_max){
6
 800295c:  b500        push  {lr}
7
 800295e:  b083        sub  sp, #12
8
#include "config.h"
9
10
11
12
static int16_t PhysicalToDigits(float physical, float factor, float offset){
13
  return (int16_t)((physical + offset) * factor);
14
 8002960:  eddf 7a09   vldr  s15, [pc, #36]  ; 8002988 <FPGA_SetLimitsI_OUT+0x2c>
15
 8002964:  ee07 0a10   vmov  s14, r0
16
 8002968:  ee77 7a27   vadd.f32  s15, s14, s15
17
 800296c:  eefe 7acf   vcvt.s32.f32  s15, s15, #2
18
}
19
void FPGA_SetLimitsI_IN(float I_IN_max){
20
  Send_SPItoFPGA(WRITE_I_IN_MAX, PhysicalToDigits(I_IN_max, CAL_I_IN_FACTOR, 0));
21
}
22
void FPGA_SetLimitsI_OUT(float I_OUT_max){
23
  Send_SPItoFPGA(WRITE_I_OUT_MAX, PhysicalToDigits(I_OUT_max, CAL_I_OUT_FACTOR, 0));
24
 8002970:  f24b 3002   movw  r0, #45826  ; 0xb302
25
 8002974:  edcd 7a01   vstr  s15, [sp, #4]
26
 8002978:  f8bd 1004   ldrh.w  r1, [sp, #4]
27
 800297c:  f7ff ff24   bl  80027c8 <Send_SPItoFPGA>
28
}
29
 8002980:  b003        add  sp, #12
30
 8002982:  f85d fb04   ldr.w  pc, [sp], #4
31
 8002986:  bf00        nop
32
 8002988:  00000000   .word  0x00000000

Die Schachtelung und die fehlende abschließende } verstehe ich auf 
anhieb nicht. Aber nach Installation von 
gcc-arm-none-eabi-10.3-2021.10-win32.exe
und full rebuild ändert sich das auf eine deutlich übersichtlichere 
Version:
1
void FPGA_SetLimitsI_OUT(float I_OUT_max){
2
 80029d0:  b508        push  {r3, lr}
3
 80029d2:  ee07 0a90   vmov  s15, r0
4
  return (int16_t)((physical + offset) * factor);
5
 80029d6:  ed9f 7a09   vldr  s14, [pc, #36]  ; 80029fc <FPGA_SetLimitsI_OUT+0x2c>
6
 80029da:  ee77 7a87   vadd.f32  s15, s15, s14
7
 80029de:  ed9f 7a08   vldr  s14, [pc, #32]  ; 8002a00 <FPGA_SetLimitsI_OUT+0x30>
8
 80029e2:  ee67 7a87   vmul.f32  s15, s15, s14
9
 80029e6:  eefd 7ae7   vcvt.s32.f32  s15, s15
10
  Send_SPItoFPGA(WRITE_I_OUT_MAX, PhysicalToDigits(I_OUT_max, CAL_I_OUT_FACTOR, 0));
11
 80029ea:  ee17 3a90   vmov  r3, s15
12
 80029ee:  b299        uxth  r1, r3
13
 80029f0:  f24b 3002   movw  r0, #45826  ; 0xb302
14
 80029f4:  f7ff ff3c   bl  8002870 <Send_SPItoFPGA>
15
}

Ich habe leider noch keine Möglichkeit das zur Laufzeit zu checken, aber 
sieht ja erstmal gut aus :)

Abschließend Frage ich mich aber noch zwecks Lessons learned:
- Pech? oder selbst schuld und in Zukunft:
- Immer neuesten Compiler installieren? (Immerhin wars die neusteste 
DAVE Version!) oder
- Keine "tote", d.h. nicht weiterentwickelte Software (DAVE) mehr 
verwenden und zu STM TI etc wechseln? oder
- Kein GCC für wirklich kritische Projekte? oder
- Testen Testen Testen, oder
?

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


Lesenswert?

Stefan M. schrieb:
> Abschließend Frage ich mich aber noch zwecks Lessons learned:

Da du die Ursache nicht finden konntest, kann man da auch kein allgemein 
gültiges Fazit ziehen. Du schiebst es jetzt auf eine bestimmte 
Compilerversion, aber ich halte das für sehr gewagt.

Um wirkklich Verhaltensregeln abzuleiten, müsstest du den Bug schon 
nochmal reproduzieren können und finden, was es genau war.

> Die Schachtelung und die fehlende abschließende } verstehe ich auf
> anhieb nicht.

Nun, das ist ein Disassembly, bei dem nachträglich anhand der 
Zeilennummern aus der Debuginformation der zugehörige Sourcecode hinein 
gemappt wird. Dass das (je nach Optimierung) zuweilen mal seltsame 
Artefakte produziert, ist nicht groß verwunderlich.

Etwas anderes wäre es, wenn du dir den vom Compiler generierten 
Assemblercode mit eingeblendetem Sourcecode ausgeben lässt. Da gibt's 
auch eine Option dafür. Wenn du aber im finalen Resultat link time 
optimization angeschaltet hast, muss das wiederum nicht mehr mit dem 
überein stimmen, was der Compiler initial als Assemblercode produziert 
hatte.

von MaWin (Gast)


Lesenswert?

Stefan M. schrieb:
> Kein GCC für wirklich kritische Projekte?

Dann steige besser nie wieder in Autos und Flugzeuge ein.

von Markus F. (mfro)


Lesenswert?

Ich glaube nicht, dass Du irgendwo einen ausgiebiger getesteten Compiler 
als gcc findest.

von Jan K. (jan_k)


Lesenswert?

MaWin schrieb:
> Stefan M. schrieb:
>> Kein GCC für wirklich kritische Projekte?
>
> Dann steige besser nie wieder in Autos und Flugzeuge ein.

Blöde Frage, aber wie sollte das gehen? Kenne keinen zertifizierten GCC 
mit ASIL. Gibt es den?

von Jan K. (jan_k)


Lesenswert?

Markus F. schrieb:
> Ich glaube nicht, dass Du irgendwo einen ausgiebiger getesteten Compiler
> als gcc findest.

Für Cortex M? Da lehne ich mich mal arg aus dem Fenster und behaupte, 
dass der Keil, der nunmal der offizielle Compiler von ARM ist, deutlich 
mehr getestet ist.

von MaWin (Gast)


Lesenswert?

Jan K. schrieb:
> Blöde Frage, aber wie sollte das gehen? Kenne keinen zertifizierten GCC
> mit ASIL. Gibt es den?

Selbstverständlich.

von Oliver S. (oliverso)


Lesenswert?

Jörg W. schrieb:
> Stefan M. schrieb:
> Wenn du aber im finalen Resultat link time
> optimization angeschaltet hast, muss das wiederum nicht mehr mit dem
> überein stimmen, was der Compiler initial als Assemblercode produziert
> hatte.

Bei link time optimization wird gar kein Assemblercode "initial" 
erzeugt. Da findet der Compilerlauf halt erst später statt.

Oliver

Beitrag #7264958 wurde vom Autor gelöscht.
von Peter D. (peda)


Lesenswert?

Stefan M. schrieb:
> Habe ich wirklich seit dem nichtmehr auf full-rebuild
> geklickt?

Das vermute ich. Da sind noch Objekte vom alten Compiler liegen 
geblieben.

Ein Kollege hatte auch mal seltsame Effekte. Die Ursache war dann, daß 
nach dem Compilerwechsel die Lib aber noch mit dem alten erzeugt gewesen 
war.

von Μαtthias W. (matthias) Benutzerseite


Lesenswert?

Jan K. schrieb:
> MaWin schrieb:
>> Stefan M. schrieb:
>>> Kein GCC für wirklich kritische Projekte?
>>
>> Dann steige besser nie wieder in Autos und Flugzeuge ein.
>
> Blöde Frage, aber wie sollte das gehen? Kenne keinen zertifizierten GCC
> mit ASIL. Gibt es den?

https://hightec-rt.com/en/products/development-platform.html

Basierend alle auf dem GCC.

von jojo (Gast)


Lesenswert?

Μαtthias W. schrieb:
> https://hightec-rt.com/en/products/development-platform.html
>
> Basierend alle auf dem GCC.

Woher weißt du das? 
https://www.google.com/search?q=gnu+site%3Ahightec-rt.com Sagt eher 
nicht so viel... Auf deren Seite steht nix von GCC oder GNU.

von MaWin (Gast)


Lesenswert?

jojo schrieb:
> Woher weißt du das?

Weil man das weiß, wenn man einmal damit gearbeitet hat.

von gcc-oder-llvm (Gast)


Lesenswert?

Μαtthias W. schrieb:
> https://hightec-rt.com/en/products/development-platform.html
>
> Basierend alle auf dem GCC.

GCC (alt) oder Clang/LLVM (neu).

von Kaj (Gast)


Lesenswert?

jojo schrieb:
> Auf deren Seite steht nix von GCC oder GNU.

Guckst du hier:

https://hightec-rt.com/en/blog/item/nxp-s32z-s32e-hightec-compiler.html
1
The HighTec C/C++ Compiler is based on innovative LLVM open-source technology...


https://hightec-rt.com/en/news/blog/item/stm-sr6x-stellar-hightec-compiler.html
1
The HighTec C/C++ compiler is based on the modern and innovative open-source technology LLVM.

von MaWin (Gast)


Lesenswert?

Kaj schrieb:
> Guckst du hier:

Das gilt nur für die neusten bleeding-edge-Varianten.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.