Forum: Compiler & IDEs GCC single vs. double precision


von AG (Gast)


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

1
#include <stdio.h>
2
#include <math.h>
3
int main()
4
{
5
    double a = 260000.303244828450260683894157409668L;
6
    double c;
7
    
8
    c = fmod(a,0.1);
9
    printf("Standard:\na: %.30lf\nc: %.30lf\n",a,c);
10
    
11
    c = fmod(a,0.1L);
12
    printf("Long:\na: %.30lf\nc: %.30lf\n",a,c);
13
    
14
  return 0;
15
}
(a ist ein typischer Wert in der Berechnung)

Ausgabe:
1
Standard:
2
a: 260000.303244828450260683894157409668
3
c: 0.099370523559628054499626159668
4
Long:
5
a: 260000.303244828450260683894157409668
6
c: 0.003244828435827767920685005265

Compiler Flags:
1
-DALT_DEBUG -O0 -g -Wall

von Jörg X. (Gast)


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...

von AG (Gast)


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.

von Jörg X. (Gast)


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

von AG (Gast)


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.

von Andreas K. (a-k)


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?

von AG (Gast)


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..."

von Andreas K. (a-k)


Lesenswert?

Kannst ja mal die Umkehrung versuchen: -fno-single-precision-constant

von AG (Gast)


Lesenswert?

Hilft leider nichts

von yalu (Gast)


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.

von yalu (Gast)


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 ...

von AG (Gast)


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...

von AG (Gast)


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!

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.