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
intmain()
4
{
5
doublea=260000.303244828450260683894157409668L;
6
doublec;
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
return0;
15
}
(a ist ein typischer Wert in der Berechnung)
Ausgabe:
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...
> 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.
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
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.
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?
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..."
> 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.
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 ...
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...
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!