Ist die wissenschaftliche Notation für ganzzahlige Konstanten in C sicher?
Lesezeit: 7 Minuten
Seit einiger Zeit stelle ich große Potenzen von 10 in Konstanten in wissenschaftlicher Notation dar, nur damit ich die Nullen nicht zählen muss. z.B
#define DELAY_USEC 1e6
Ein Kollege wies darauf hin, dass dies nicht sicher ist, da es sich nicht um eine Ganzzahl handelt und nicht garantiert immer gleich 1000000 ist exakt. Die Dokumentation scheint dies zu bestätigen, aber ich frage mich, ob es in der Praxis wahr ist. Gibt es eine Möglichkeit, eine Zehnerpotenz-Ganzzahl mit einer Abkürzung sicher zu deklarieren? Ist es sicher, es einfach in ein int in der Definition umzuwandeln?
Sie können die Zehnerpotenz als Ganzzahl notieren und davon ausgehen, dass sie eins ist (z. B. durch Namenskonvention) und sie bei der Ausführung des Codes einfach an eine echte Zehnerpotenz übergeben, das ist sicher.
– Jan
24. Juni 2014 um 14:50 Uhr
Sie wandeln es nicht in ein int in der Definition um. Wenn Sie aus Gründen der Typsicherheit eine ganzzahlige Konstante wünschen und diese der Kürze halber abkürzen, können Sie ein hexadezimales Literal verwenden.
– StoryTeller – Unslander Monica
24. Juni 2014 um 14:52 Uhr
Ich denke, weil Sie, da Sie einen Float definieren, keine Möglichkeit haben, zu wissen, ob es genau so sein wird, da Gleitkommazahlen eine begrenzte Genauigkeit haben.
– Ben
24. Juni 2014 um 14:52 Uhr
Dies ist keine Antwort auf Ihre Frage, aber denken Sie daran, dass Sie Mathematik verwenden können, wenn Sie Konstanten wie definieren static const int SEC_TO_MILLI = 1000; static const int SEC_TO_MICRO = 1000 * SEC_TO_MILLI; Dies hilft mir oft, komplizierte, fehleranfällige Zahlenliterale zu vermeiden.
– Japreis
24. Juni 2014 um 14:54 Uhr
Ein weiteres Problem dabei ist, dass jeder Ausdruck, zu dem er gehört, mit „doppelter“ Genauigkeit ausgewertet wird, was wahrscheinlich nicht das ist, was Sie wollen.
– Lindydancer
24. Juni 2014 um 14:58 Uhr
Mike Seymour
Theoretisch nein. Keine der Sprachen gibt an, wie Gleitkommawerte dargestellt werden oder welche Werte genau dargestellt werden können. (UPDATE: anscheinend empfiehlt C11 eine Darstellung. C++ und ältere C-Dialekte tun dies nicht).
In der Praxis ja, für einen recht großen Wertebereich. Jede Implementierung, auf die Sie wahrscheinlich stoßen werden, verwendet a 64-Bit-IEEE-Darstellung zum double. Dies kann jeden ganzzahligen Wert bis 2 darstellen53 (ungefähr 9×10fünfzehn) exakt. Es kann sicherlich alles darstellen, was durch einen 32-Bit-Ganzzahltyp darstellbar ist.
C11 5.2.4.2.2 legt einige Einschränkungen fest. zB ein double muss jede ganze Zahl mit 10 Dezimalstellen aufnehmen können.
– Mafso
24. Juni 2014 um 15:01 Uhr
@mafso: Interessant, das wusste ich nicht. Das bedeutet, dass jeder moderne C-Compiler jeden 32-Bit-Ganzzahlwert genau darstellen sollte. In C++ gibt es (noch) keine solche Einschränkung, es sei denn, ich habe sie übersehen.
– Mike Seymour
24. Juni 2014 um 15:22 Uhr
Ich weiß nichts über C++, aber C gibt tatsächlich an, dass Gleitkommawerte nach Möglichkeit IEEE 754 verwenden sollen (C11 Annex F). Eine konforme Implementierung soll dies tun #define Flags, um die richtige Unterstützung anzuzeigen, sodass diese Informationen dem Programmierer zur Verfügung stehen.
– Leuschenko
24. Juni 2014 um 15:27 Uhr
@Leushenko: Danke, meine C-Kenntnisse sind etwas alt; Ich wusste nicht, dass das heutzutage vorgeschrieben war.
– Mike Seymour
24. Juni 2014 um 15:29 Uhr
Tatsächlich können Sie für positive Potenzen von 10 problemlos große Werte bis 1e22 verwenden: So überraschend es auch sein mag, sie sind immer noch exakt im 53-Bit-Signifikanz- und Fließkommaformat darstellbar (versuchen Sie einfach, sie mit einer anständigen Implementierung von printf). Das ist weil log2(5^22)<52und 1e22==5^22*2^22.
– Ruslan
28. Mai 2018 um 19:57 Uhr
Paul Evans
Sie möchten benutzerdefinierte Literale verwenden:
constexpr long long operator "" _k(long long l) {
return l * 1000;
}
constexpr long long operator "" _m(long long l) {
return l * 1000 * 1000;
}
dann kannst du einfach machen:
long long delay = 1_m;
long long wait = 45_k;
Aber Sie werden sie anrufen wollen _k und _m, da wörtliche Namen ohne führende Unterstriche reserviert sind. Vielleicht möchten Sie auch _M statt _m passend zum SI-Präfix.
– Mike Seymour
24. Juni 2014 um 15:02 Uhr
@MikeSeymour du hast recht, Antwort entsprechend geändert
– Paul Evans
24. Juni 2014 um 15:15 Uhr
@MikeSeymour – ist ein Unterstrich gefolgt von Großbuchstaben nicht eine reservierte Kennung? (Nicht wörtlich reserviert, aber allgemein für die Implementierung reserviert.)
– Martin B
24. Juni 2014 um 20:43 Uhr
@Mike – hmmm … aber für “normale” reservierte Symbole darf die Implementierung ein Makro _M definieren (da das reserviert ist) und so möchten Vermasseln Sie den Benutzercode. Das scheint also seltsam für Benutzerdef.
– Martin B
25. Juni 2014 um 7:20 Uhr
@MartinBa: Ja, du hast recht; _M wäre nicht erlaubt. Das hätte ich nicht vorschlagen sollen.
– Mike Seymour
25. Juni 2014 um 10:04 Uhr
tmyklebu
Sie fragen speziell nach Zehnerpotenzen. 1e6 wird genau eine Million sein. Sie können bis zu gehen 1e22 ohne dass etwas schlimmes passiert. Beachten Sie jedoch, dass sowohl in C++ als auch in C 1e6 ist ein double Konstante und nicht eine ganzzahlige Konstante.
Negative Zehnerpotenzen sind eine andere Geschichte. 1e-1 ist ungenau, wie alle niederen Mächte.
C garantiert dies nicht, Bit IEEE tut es
– Steve Cox
24. Juni 2014 um 14:53 Uhr
Nicht alle niederen Mächte: 5e-1 ist genau.
– James Kanze
24. Juni 2014 um 14:55 Uhr
@JamesKanze: Es ist auch keine Zehnerpotenz. (Ich habe den Text nur korrigiert, um zu verdeutlichen, dass ich von Zehnerpotenzen spreche.)
– tmyklebu
24. Juni 2014 um 14:56 Uhr
Woher kommt der Wert von 1e22 komme aus? Nach meiner Berechnung ist die kleinste nicht darstellbare ganze Zahl 2^53+1etwa 1e16.
– Mike Seymour
24. Juni 2014 um 15:13 Uhr
@tmyklebu: Fair genug, ich habe diese Einschränkung nicht bemerkt. Es könnte sich lohnen, die Antwort zu klären, falls ich nicht der einzige bin, der sie ohne den vollständigen Kontext liest.
– Mike Seymour
24. Juni 2014 um 15:18 Uhr
Es scheint, dass gcc nimmt eine in wissenschaftlicher Notation definierte Konstante als Fließkommazahl an, sofern sie nicht gecastet wird.
Wie gesagt hier10e15 und 10e22 sind die maximale Potenz von zehn Zahlen, die eine exakte Darstellung im Gleitkommaformat mit einfacher bzw. doppelter Genauigkeit haben.
Größere Zehnerpotenzen können nicht mit 32-Bit- oder 64-Bit-Ganzzahltypen dargestellt werden.
Sie werden nie Rundungsfehler bei etwas weniger als bekommen INT_MAXda die Spezifikation für double legt beiseite 52 Bits für Sie zu verwenden. Ihre “Bruchkomponente” ist nur Ihre ganze Zahl, und Ihr “Exponent” ist 1, und Gleitkommazahlen haben damit keine Probleme.
Das ist nicht ganz das, was vor sich geht, da Sie dadurch eine nicht normalisierte Gleitkommazahl erhalten. double hat ein implizites Bit, daher können Sie positive ganze Zahlen nicht auf diese Weise darstellen. (Stattdessen erhalten Sie das normalisierte Äquivalent dessen, was Sie gesagt haben.)
– tmyklebu
24. Juni 2014 um 14:55 Uhr
Wie meinst du das? Der Wert im “Exponenten”-Bitbereich der Zahl wird nicht wirklich 1 sein, aber ich glaube, dass das Ergebnis so funktioniert, dass der “Bruch” nur Ihre Zahl enthält. Solange Sie unter 2 ^ 32 sind (was ich für das OP nehme, da er wahrscheinlich vorsichtiger wäre, wenn er größere Konstanten verwenden müsste), sollte das gelten.
– Patrick Collins
24. Juni 2014 um 15:01 Uhr
Der Signifikand enthält alles bis auf das erste Bit Ihrer Zahl. Das höherwertige Bit ist das implizite Bit.
– tmyklebu
24. Juni 2014 um 15:01 Uhr
Chemseddine
Es ist wirklich nicht sicher, weil der Compiler es als Fließkommazahl betrachtet, daher ist die Genauigkeit auf 53 Bit statt 64 Bit von Ganzzahlen (long int) begrenzt. Weitere Informationen zur Darstellung von Fließkommazahlen finden Sie hier
Das ist nicht ganz das, was vor sich geht, da Sie dadurch eine nicht normalisierte Gleitkommazahl erhalten. double hat ein implizites Bit, daher können Sie positive ganze Zahlen nicht auf diese Weise darstellen. (Stattdessen erhalten Sie das normalisierte Äquivalent dessen, was Sie gesagt haben.)
– tmyklebu
24. Juni 2014 um 14:55 Uhr
Wie meinst du das? Der Wert im “Exponenten”-Bitbereich der Zahl wird nicht wirklich 1 sein, aber ich glaube, dass das Ergebnis so funktioniert, dass der “Bruch” nur Ihre Zahl enthält. Solange Sie unter 2 ^ 32 sind (was ich für das OP nehme, da er wahrscheinlich vorsichtiger wäre, wenn er größere Konstanten verwenden müsste), sollte das gelten.
– Patrick Collins
24. Juni 2014 um 15:01 Uhr
Der Signifikand enthält alles bis auf das erste Bit Ihrer Zahl. Das höherwertige Bit ist das implizite Bit.
– tmyklebu
24. Juni 2014 um 15:01 Uhr
14082600cookie-checkIst die wissenschaftliche Notation für ganzzahlige Konstanten in C sicher?yes
Sie können die Zehnerpotenz als Ganzzahl notieren und davon ausgehen, dass sie eins ist (z. B. durch Namenskonvention) und sie bei der Ausführung des Codes einfach an eine echte Zehnerpotenz übergeben, das ist sicher.
– Jan
24. Juni 2014 um 14:50 Uhr
Sie wandeln es nicht in ein int in der Definition um. Wenn Sie aus Gründen der Typsicherheit eine ganzzahlige Konstante wünschen und diese der Kürze halber abkürzen, können Sie ein hexadezimales Literal verwenden.
– StoryTeller – Unslander Monica
24. Juni 2014 um 14:52 Uhr
Ich denke, weil Sie, da Sie einen Float definieren, keine Möglichkeit haben, zu wissen, ob es genau so sein wird, da Gleitkommazahlen eine begrenzte Genauigkeit haben.
– Ben
24. Juni 2014 um 14:52 Uhr
Dies ist keine Antwort auf Ihre Frage, aber denken Sie daran, dass Sie Mathematik verwenden können, wenn Sie Konstanten wie definieren
static const int SEC_TO_MILLI = 1000; static const int SEC_TO_MICRO = 1000 * SEC_TO_MILLI;
Dies hilft mir oft, komplizierte, fehleranfällige Zahlenliterale zu vermeiden.– Japreis
24. Juni 2014 um 14:54 Uhr
Ein weiteres Problem dabei ist, dass jeder Ausdruck, zu dem er gehört, mit „doppelter“ Genauigkeit ausgewertet wird, was wahrscheinlich nicht das ist, was Sie wollen.
– Lindydancer
24. Juni 2014 um 14:58 Uhr