mikrocontroller.net

Forum: Compiler & IDEs GCC single vs. double precision


Autor: AG (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe bei compilieren des folgenden Codes das Problem, dass die 
Konstanten im Code als single precision floats interpretiert werden. 
Sämtliche Docs die ich gefunden habe, deuten darauf hin, dass der 
Compiler die eigentlich per default als double übersetzen sollte. Ich 
verwende GCC 3.4.4-3 für den Altera Nios2 Prozessor. Die Option 
-fsingle-precision-constant ist nicht aktiviert.

Nachdem es sich bei dem Projekt um ca. 20000 Zeilen Code handelt, habe 
ich keine sehr grosse Lust, alle Zeilen durchzugehen und überall wo es 
kritisch werden könnte ein L hinzuschreiben. Wie kann ich dem GCC 
beibringen, dass er alle Konstanten als double verwendet?

Gruss, Andreas

#include <stdio.h>
#include <math.h>
int main()
{
    double a = 260000.303244828450260683894157409668L;
    double c;
    
    c = fmod(a,0.1);
    printf("Standard:\na: %.30lf\nc: %.30lf\n",a,c);
    
    c = fmod(a,0.1L);
    printf("Long:\na: %.30lf\nc: %.30lf\n",a,c);
    
  return 0;
}
(a ist ein typischer Wert in der Berechnung)

Ausgabe:
Standard:
a: 260000.303244828450260683894157409668
c: 0.099370523559628054499626159668
Long:
a: 260000.303244828450260683894157409668
c: 0.003244828435827767920685005265

Compiler Flags:
-DALT_DEBUG -O0 -g -Wall

Autor: Jörg X. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum schmeißt du denn die Begriffe "float", "double", "long" einfach so 
durcheinander?
- AFAIK sind alle Konstanten in denen  ein "." oder "e" bzw "E" vorkommt 
(0.1, -0.000005, 12430512e200) vom Typ  double (= 64Bit, wenn's 
Ieee7xxx (hab ich grad nicht im Kopf)kompatible sind).
- Wenn da ein "F" angehängt wird  (z.B. 0.00001F, 23.768e-12F, ) werden 
es float -Werte  (32Bit, nach der IEEE-Norm)
- Wenn du an eine int -Konstante (1, 04, -1827, 0xdeadbeef) ein 'L' 
anhängst, wird's ein "long" (Größe von int/long sind 'implementation 
dependant': 16/32Bit beim AVR-GCC, 32/32Bit beim Microsoft Visual c++ 
Compiler etc.)
Was bei 0.234L rauskommt weiß ich nicht, sollte aber - wenn's nach mir 
ginge - 0 oder ein Error werden.
hth. Jörg
ps.:
> Nachdem es sich bei dem Projekt um ca. 20000 Zeilen Code handelt, habe
> ich keine sehr grosse Lust, alle Zeilen durchzugehen und überall wo es
> kritisch werden könnte ein L hinzuschreiben.
Deshalb schreibt man keine 'Magic-numbers' in den Sourcecode...

Autor: AG (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wenn du an eine int -Konstante (1, 04, -1827, 0xdeadbeef) ein 'L'

Ich arbeite mit Gleitkomma-Werten.


> Was bei 0.234L rauskommt weiß ich nicht, sollte aber - wenn's nach mir
> ginge - 0 oder ein Error werden.

Es geht aber nicht nach Dir, sondern nach dem GCC. Und eine rein 
empirische Überprüfung (siehe Ausgabe oben) ergibt, dass 0.1 im Code als 
single precision floating point eingebaut wird, 0.1L jedoch als double 
precision floating point. Dieses Verhalten würde ich gerne mit einem 
compiler flag beeinflussen.


> Deshalb schreibt man keine 'Magic-numbers' in den Sourcecode...

Tut mir fürchterlich leid, aber ich hab den Code nicht geschrieben. Ich 
muss nur damit leben. Im übrigen ist es völlig belanglos, ob da Nummern 
stehen oder ein #define verwendet wird. Das Ergebnis bleibt das gleiche.

Damit wenigstens Dir geholfen ist, die Norm nennt sich IEEE 754.

Autor: Jörg X. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tut mir Leid, an den Typ "long double" hatte ich gar nicht gedacht. Das 
macht meinen anderen Post ja beinahe Gegenstandslos :(
Aber evtl. Fällt mir ja noch was Produktives ein: z.B. beim AVR-GCC sind 
double nur als 32Bit-Zahlen (d.h. float) implementiert, evtl. ist das 
bei deinem GCC ja auch so (würde aber deine Ausgabe nicht erklären).

sorry nochmal, Jörg

Autor: AG (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab mir die Genauigkeiten nochmal angeschaut. Wenn ich im code 0.1 
schreibe, wird dies mit 0.10000000149... ersetzt, was single precision 
float entspricht. 0.1L wird zu 0.10000000000000000555..., dies ist 
konsistent mit einer double precision float Zahl.

Die double Variablen sind auch tatsächlich in 64Bit implementiert, sonst 
würde nämlich in meinem Code überhaupt nichts funktionieren :-)

Leider finde ich im Internet übehaupt keine Aussagen dazu, einzig der 
flag -fsingle-precision-constant legt nahe, dass per default die 
Konstanten eigentlich als double precision eingesetzt werden sollten.

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
AG wrote:

> dass per default die Konstanten eigentlich als double
> precision eingesetzt werden sollten.

Das ist in C auch so definiert.

Fehlt evtl. irgendeine Library, so dass falsche Funktionen reingezogen 
werden?

Autor: AG (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gute Frage. Ich habe die fmod() überprüft, und die tut auf jeden Fall 
das was sie soll, nämlich modulo in double precision zu rechnen. Für das 
einsetzen der Konstanten wüsste ich jetzt nicht, welche Library da 
zuständig sein soll, wenn überhaupt.

Meinen Tests nach muss es schlichtweg daran liegen, dass der Compiler, 
sofern man ihn nicht mit L zwingt, einfach single precision floats aus 
Konstanten macht.
Kann natürlich auch sein, dass Altera beim portieren des gcc auf den 
Nios gepfuscht hat. Leider beschränkt sich der Support da auf "gcc 
supporten wir nicht..."

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kannst ja mal die Umkehrung versuchen: -fno-single-precision-constant

Autor: AG (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hilft leider nichts

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Die double Variablen sind auch tatsächlich in 64Bit implementiert,
> sonst würde nämlich in meinem Code überhaupt nichts funktionieren

Als erstes hätte ich auch vermutet, dass Altera float und double beide
32 Bit und long double 64 Bit groß gemacht hat, auch wenn so etwas
ziemlich  unüblich wäre. Aber wenn du festgestellt hast, dass double
64 Bit groß ist, ist diese Vermutung natürlich widerlegt.

> Kann natürlich auch sein, dass Altera beim portieren des gcc auf den
> Nios gepfuscht hat.

Darauf würde ich fast tippen. Da das Laufzeitsystem offensichtlich
keine größere Genauigkeit als 64 Bit unterstützt, wurde long double
mit double gleichgesetzt. Ohne mich in den GCC-Interna auszukennen,
vermute ich, dass dies mindestens an zwei Stellen gemacht werden
musste, nämlich im Code-Genarator und im Lexer. In ersterem verlief
die Änderung fehlerfrei, bei letzterem könnten die Jungs etwas über's
Ziel hinausgeschossen sein und die Konvertierung von double-Konstanten
gleich mit geändert haben.

Zum Glück ist der Compiler Open Source, da kannst du doch nachschauen
;-)

Selbst wenn Altera keinen GCC-Support macht, könnte es vielleicht
sein, dass man dort Bug-Reports entgegen nimmt, da ja evtl. auch
zukünftige GCC-Versionen portiert werden sollen. Das bringt dir zwar
auf die Schnelle nichts, aber vielleicht erfährst du dabei, ob es sich
wirklich um einen Bug handelt.

Arg viel weiterhelfen konnte ich dir natürlich auch nicht.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielleicht ist es doch ein Feature und kein Bug. Hier

  http://www.altera.com/literature/es/es_nios2eds_60.pdf

steht u.a. geschrieben:

  "Double-precision Floating-Point operations with floating-point
  custom instructions

  Calls to double-precision floating-point functions in math.h will
  return less-precise results on Nios II processors using the
  floating-point custom instruction. Floating-point constants are
  forced to single-precision when the floating-point custom
  instructions is present, which affects the constants for the
  double-precision floating-point functions in libm."

Ich verstehe den Abschnitt zwar nicht, weil ich keine Ahnung vom Nios
II, geschweige denn von "floating-point custom instructions" habe,
aber ich könnte mir vorstellen, dass man irgendwo zwischen custom und
standard instruktions umschalten kann.

Oder auch nicht, war nur so ein Gedanke ...

Autor: AG (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bisher waren Compiler für mich immer eine Black Box, vorne Code rein, 
hinten binary raus. Recht viel mehr Ahnung hab ich davon nicht und ich 
trau mich ehrlich gesagt nicht ran, da irgend was zu verändern. Da werd 
ich wohl noch ein paar Diskussionen mit dem Altera Support führen müssen 
:-)

Das mit den Custom Instructions ist ein guter Punkt, das war mir bisher 
noch nicht aufgefallen. Ich denke da muss ich gleich am Montag früh mal 
nachschauen ob meine CPU eine single-precision FPU dran hat, ich meine 
mich da an was erinnern zu können...
Das ganze bietet dir die Möglichkeit, an den Prozessor zusätzlich zb. 
eine FPU oder eine Wurzelzieheinheit dranzubauen. Diese kann dann mit 
gcc-flags (-mcustom-operation=opcode) aktiviert werden und der Compiler 
setzt den Code dann so um, dass diese Hardware benutzt wird anstatt der 
Software-Emulation. Eigentlich kein Hexenwerk aber verheerend schlecht 
dokumentiert...

Autor: AG (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tatsache, wenn der Prozessor eine single precision FPU dran hat werden 
sämtliche Konstanten auf single precision begrenzt.
Dazu ist es anscheinend völlig unerheblich ob die FPU überhaupt benutzt 
wird, d.h. selbst ohne die Compiler-Flags, die die entsprechenden 
Opcodes aktivieren verschwindet die Präzision. Wär schon toll wenn der 
Compiler da wenigstens ne Warnung ausgeben würde, aber man kann wohl 
nicht alles haben.

Vielen Dank für eure Hilfe!

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]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [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.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

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