Zum Inhalt springen

Problem(e) mit 64-Bit-Typen


DITTY

Empfohlene Beiträge

Hallo COM,

zunächst einmal der Bsp.-Code: Test64_1.cpp

#include <climits>

#include <cstdint>

#include <cinttypes>

#include <cfloat>

#include <cwchar>

#include <string>  //std:string

#include <string.h>

#include <cstring>

#include <cstdlib>

#include <cstdio>

#include <stdio.h>

#include <iostream>

#include <cmath>


#ifdef _OPENMP

#include <omp.h>

#endif


using namespace std;


int main(int argc, char *argv[])

{

  uint64_t maxof = 0-1;

  const uint64_t NumValues = 9223372036854775807;

  uint64_t NumIterSingle = 0;


  for(uint64_t i=0;i<NumValues;i++)

  {

    NumIterSingle++;

  }

  printf("Benötigte Iterationen - SERIELL:  %"PRIu64"\n",NumIterSingle);

  cout << "Referenz:                         " << NumValues << '\n';

  cout << "MaxOf uint64_t:                   " << maxof << '\n';


  getchar();

  return 0;

}
Compile mit:
g++ -std=c++0x -m64 -fopenmp -Wall -Wextra -pedantic -pedantic-errors -O3 -s source -o dest
Funktioniert. Zum Hintergrund: Ich möchte mit 64-Bit-Typen arbeiten. Ich bevorzuge die int64_t-Typen, weil ich fest definierte 64-Bit-Typen benötige und auf long bzw. long long, wollte ich nicht zurückgreifen, da systemabhängig. Ist ja auch egal, da die *_t-Typen lediglich typedef´s von bekannten Typen, wie long bzw. long long, sind. Im Gegensatz zu den eben genannten normalen Typen, welche je nach Compiler, Compiler-Option oder auch nach System unterschiedlich groß sein können, sind die *_t-Typen überall und unabhängig von Compiler, Compiler-Option und System, in ihre[r][n] Größe / Wertebereich fest definiert. Übersicht: signed 64-Bit: −9223372036854775808 bis 9223372036854775807 unsigned 64-Bit: 0 bis 18446744073709551615 Obiges Programm bestätigt dies für unsigned 64-Bit (uint64_t maxof = 0-1; ). Soweit alles richtig. Sobald ich aber für NumValues einen größeren Wert als 9223372036854775807 verwende, z.B. 9223372036854775808, wird zwar kompiliert und ein exec-File erzeugt, aber dieses bleibt dann quasi irgendwie hängen, wenn man es ausführt. Das Verhalten kann ich irgendwie nicht ganz nachvollziehen. Ebenso verändert sich der Compiler-Output bei einem Wert größer 9223372036854775807:
Test64_1.cpp:28:30: warning: integer constant is so large that it is unsigned

Test64_1.cpp:28: warning: this decimal constant is unsigned only in ISO C90
Mir ist schon bewusst, was diese Warnungen aussagen wollen, ich kann es nur nicht nachvollziehen. Bis zum Wert 9223372036854775807 scheints ja zu funktionieren. Ich versteh es dahingehend nicht, weil 9223372036854775807 lediglich das Maximum für signed 64-Bit (also int64_t) ist, ich aber unsigned 64-Bit (uint64_t) verwende, wo z.B. auch 9223372036854775808 locker hineinpassen sollte. Der erste Test funktioniert ja und ein:
uint64_t maxof = 0-1;

offenbart, dass Werte größer 9223372036854775807 (max. bis 18446744073709551615), angenommen werden und funktionieren.

Warum bleibt das Programm förmlich stehen? Ich meine, es tut sich minutenlang nichts, sodass ich mit STRG+C beenden muss.

Hat jemand Vorschläge, Lösungen, etc.?

EDIT:

Es muss etwas mit der for()-Schleife zu tun haben. Wenn ich diese auskommentiere, funktioniert nämlich alles, sprich, z.B. wird NumValues = 9223372036854775808, am Ende auch korrekt ausgegeben. Nur weshalb das mit ner for()-Schleife nicht funktionieren sollte, bleibt mir ein Rätsel!???!

Ich meine 9223372036854775807 funktioniert, auch mit der for()-Schleife.

Besten Dank im Voraus!

Grüße

DITTY :):):):)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Eine Kleine Rechnung: Stell dir vor dein Prozessor braucht für eine Schleifeniteration 20 Takte.

Macht 9.223.372.036.854.775.807 * 20 Takte die insgesamt benötigt werden. Nehmen wir an dein Prozessor macht 2,6 GHz also 2.600.000.000 Takte pro Sekunde dann brauchst du immernoch 9.223.372.036.854.775.807 * 20 / 2.600.000.000 Sekunden bis du fertig bist. Mein Taschenrechner gibt für diese Rechnung das Ergebnis 70949015668,113660053846153846154 Sekunden aus. In Minuten noch 1182483594,4685610008974358974359 . In Stunden dann 19708059,907809350014957264957265 und in Tagen dann 821169,16282538958395655270655271. In Jahren wären das dann 2281,025452292748844323757518202 immerhin noch 2281 Jahre (bei bänkischen 360 Tage / Jahr). Er bleibt nicht stehen, er macht schon was du sagst. Nur dauert das ein wenig.

Angenommen meine erste Behauptung ist falsch und er braucht nur 10 Takte sinds aber immernoch > 1000 Jahre.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn ich mir Dein Posting anschaue http://www.fachinformatiker.de/c-c/149613-fragen-zu-128-bit-integer.html

dann ist für mich die Frage was Du da überhaupt machen willst, denn einfach ein paar Zahlen durchlaufen zu lassen, ist irgendwie sehr unverständlich, vor allem weil man die Größe der Datentypen einfach über die Limits abfragen kann, ebenso wie Min / Max des Datentyps.

Link zu diesem Kommentar
Auf anderen Seiten teilen

@Leimy84:

naja. Grundsätzlich haste Recht. Fakt ist aber, dass das Programm bei mir in kürzester Zeit abgearbeitet wird (vermutlich durch Compiler optimiert: Option -O3 ist nämlich an). Und dann erklärt das trotzdem nicht dieses Verhalten. Ich meine, mit:

9223372036854775807 funktionierts

UND mit:

9223372036854775808 funktionierts nicht mehr.

Bzw. was heißt "funktioniert nicht mehr"? Wie schon erwähnt, geht Ersteres in einem kurzen Rutsch durch. Letzteres benötigt Ewigkeiten (kA, wie lange, breche immer nach mehreren Minuten ab).

Der Unterschied ist doch gerade mal eine einzige Iteration. Deshalb kann ich mir dieses Verhalten einfach nicht erklären. :confused:

@flashpixx:

Ja, na klar macht das Bsp.-Prog wenig Sinn. Die Grenzen auszulesen, ob per Lib-Variablen oder rechnerisch ist nicht das Problem. Das ist aber auch nicht das, was ich wissen möchte. Ich bin nur über die Verhaltensweisen unter entsprechenden Konstellationen verwundert und würde gerne die Gründe wissen bzw. wie man es besser / richtig machen müsste. Es müssen auf jeden Fall 64-Bit-Werte sein, da 32-Bit für meine späteren Anwendungszwecke (Physik, Astrophysik) einfach nicht mehr ausreichen. Selbst 64-Bit-Werte kommen da ab und zu an ihre Grenzen, weshalb ich später dann auch auf 128-Bit ausweichen muss. Bevor ich aber auf Drittlibs a´la GMP und Boost zurückgreife, wollte ich in erster Linie gerne erst einmal auf Möglichkeiten der Std-Libs und Compiler zurückgreifen.

----------------------------

So, nun zurück zum Thema. Jetzt bin ich ganz perplex.

mit bzw. ab:

9223372036854775809

geht es wieder.

Nochmals zum Vergleich:

bis 9223372036854775807      // keine Probleme

    9223372036854775808      // Problem

ab  9223372036854775809      // keine Probleme

Und jetzt erklärt mir das mal jemand???

Ich meine, ja gut, lasse ich eben den Wert weg, der Probleme bereitet. Aber das ist nicht gerade das Gelbe vom Ei. Woher soll man in Zukunft bei Berechnungen in diesen Größenordnungen denn wissen, welche Werte man meiden sollte und welche nicht?!?

Grundsätzlich verstehe ich einfach nicht das Problem. Ist es Eigenverschulden, ne Macke des Compilers, der Compiler-Optionen, der verwendeten Libs, oder oder oder???

Ich würde gerne einfach mal mit meiner eigentlichen Programmierung anfangen wollen, nur dummerweise baut vieles auf den Umgang mit Datentypen auf. Selbst, wenn ich ne Drittlösung als Ausweg habe, etwa Drittlibs, würde ich gerne aus reiner Interesse dem nachgehen wollen. Die Frage hier ist ja schließlich nicht, eine Drittlösung zu finden, sondern mit den gegebenen Mitteln die Problemursachen ausfindig zu machen und ggfl. zu korrigieren bzw. richtig anzuwenden.

Ich kanns mir nicht erklären. Vlt. ja einer von Euch. THX schon mal im Voraus!

Grüße

DITTY :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich bin nur über die Verhaltensweisen unter entsprechenden Konstellationen verwundert und würde gerne die Gründe wissen bzw. wie man es besser / richtig machen müsste.

Warum liest Du nicht die entsprechende Compilerdefinition !?

Es müssen auf jeden Fall 64-Bit-Werte sein, da 32-Bit für meine späteren Anwendungszwecke (Physik, Astrophysik) einfach nicht mehr ausreichen. Selbst 64-Bit-Werte kommen da ab und zu an ihre Grenzen, weshalb ich später dann auch auf 128-Bit ausweichen muss. Bevor ich aber auf Drittlibs a´la GMP und Boost zurückgreife, wollte ich in erster Linie gerne erst einmal auf Möglichkeiten der Std-Libs und Compiler zurückgreifen.

Nein man kann auch diese Werte mit 32 Bit darstellen, man muss sich dann nur mal etwas Gedanken über die Darstellung und Skalierung machen: Gleitkommazahl und über ein entsprechendes Klassendesign mit überladenen Operatoren ist selbst eine Berechnugn damit trivial umzusetzen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Warum liest Du nicht die entsprechende Compilerdefinition !?
Hab ich doch schon längst. Ebenso Frau Google und Herr Bing bemüht. Auch hab ich schon diverse Foren-SuFu´s bemüht. Mein Anliegen hier gibts schließlich nur, weil ich nicht mehr weiter weiß.

Nein man kann auch diese Werte mit 32 Bit darstellen, man muss sich dann nur mal etwas Gedanken über die Darstellung und Skalierung machen: Gleitkommazahl und über ein entsprechendes Klassendesign mit überladenen Operatoren ist selbst eine Berechnugn damit trivial umzusetzen.
Ja natürlich geht das. Produziert das dann nicht aber deutlich mehr Overhead und widerspricht dies dann nicht der 64-Bit-Programmierung? Ich meine: 64-Bit-Architektur vorhanden, ebenso: 64-Bit-System, 64-Bit-Compiler, 64-Libs und auch 64-Bit-INT-Typen. Warum sollte ich nicht davon Gebrauch machen dürfen???

Ich habe das Ganze jetzt noch um die INT-Literale ergänzt. Der Form halber, hier nochmal der aktuelle Code:

#include <climits>

#include <cstdint>

#include <cinttypes>

#include <cfloat>

#include <cwchar>

#include <string>  //std::string

#include <string.h>

#include <cstring>

#include <cstdlib>

#include <cstdio>

#include <iostream>

#include <cmath>


#ifdef _OPENMP

#include <omp.h>

#endif


using namespace std;


int main(int argc, char *argv[])

{

  uint64_t maxof = 0-1;

  const uint64_t NumValues = <Wert>ULL;

  uint64_t NumIterSingle = 0ULL;


  for(uint64_t i=0ULL;i<NumValues;i++)

  {

    NumIterSingle++;

  }

  printf("Benötigte Iterationen - SERIELL:  %"PRIu64"\n",NumIterSingle);

  cout << "Referenz:                         " << NumValues << '\n';

  cout << "MaxOf uint64_t:                   " << maxof << '\n';


  getchar();

  return 0;

}
Compile (Release):
g++ -std=c++0x -m64 -fopenmp -Wall -Wextra -pedantic -pedantic-errors -O3 -s source -o dest
Compile (Debug):
g++ -std=c++0x -m64 -fopenmp -Wall -Wextra -pedantic -pedantic-errors -g -ggdb3 source.cpp -o dest
g++-Version (gcc):
gcc version 4.4.5 20110214 (Red Hat 4.4.5-6) (GCC) x86-64
Zur Erinnerung: setze man folgende Werte für "NumValues" ein:
bis 9223372036854775807      // keine Probleme

    9223372036854775808      // Problem

ab  9223372036854775809      // keine Probleme

Übersetzt wurde dabei mit Compile (Release).

Wenn ich den Optimierungsschalter "-O3" weglasse, tritt das beschriebene Phänomen bei allen Zahlen auf. Lediglich bei Anwendung von "-O3", macht sich mir ein Fehlverhalten bemerkbar (siehe 9223372036854775808).

Wie schon erwähnt, scheint der Compiler lediglich beim Wert 9223372036854775808 für NumValues, nicht richtig zu optimieren, da die Ausführungszeit Ewigkeiten dauert. Es verwirrt mich dahingehend, dass es nur bei dem Wert 9223372036854775808 auftritt (als wenn da nicht optimiert wurde). Alle anderen Werte, sprich bis 9223372036854775807 und ab 9223372036854775809 bis einschließlich dem Maximalwert von unsigned 64-Bit-INT: 18446744073709551615, funktionieren (das Programm wird also in kürzester Zeit in einem Rutsch abgearbeitet).

Keine 128-Bit-Typen. Diesmal setze ich ausschließlich 64-Bit-Typen ein. Dabei ist es übrigens egal, ob ich die festen Typen a´la int64_t oder [unsigned] long long int verwende. Das Fehlverhalten bleibt das Gleiche. Das verwundert natürlich nicht, da die festen Typen auf 64-Bit-Systemen lediglich eine Typdefinition der normalen Typen sind (in meinem Fall also von unsigned long long int. Deshalb auch das Literal ULL).

Ob mit oder ohne Literale, das Problem bleibt.

Doch scheint mein Compiler diese Zahlen auch ohne Literale zu schlucken. Spuckt dann zwar Warnungen, aber es funktioniert. Wie Ihr oben seht, hab ich es dennoch der Richtigkeit halber mit Literale gemacht. Aber zum Vergleich: 9223372036854775809 und aufwärts funktionieren auch ohne Literale.

Die Aktuelle Version von g++ ist eigentlich die 4.6.1 bzw. gar schon die Version 4.7. Wie oben erwähnt, ist die auf meinem System Aktuelle, noch die 4.4.5 (aktuellste aus den Paketquellen).

Die Frage ist nun, ob das ein Eigenfehler von mir ist oder technisch bedingt bzgl. des Compilers, der Compiler-Option(en) und / oder der verwendeten Bilbiotheken?

Kann jemand meinen Fall unter Verwendung obigen Codes und Compiler-Version+Schlatern überhaupt reproduzieren und ggfl. als Vergleich das Ganze mit einem anderen Compiler oder neuerer Compiler-Version ausprobieren, ob es das Fehlverhalten da immer noch gibt? THX!

Und falls jemand das auf einen technischen Hintergrund beschränken kann, wo man selbst halt keine Handhabe mehr hat, was hättet Ihr dann noch für Tipps für mich, wie ich das Problem IRGENDWIE umschiffen kann? Ich muss nämlich definitiv mit solchen Größenordnungen hantieren.

Besten THX und Grüße

DITTY :)

PS: Debugger hab ich auch schon verwendet, nur geht da alles reibungslos von statten. Ich kann auch mit dem Debugger nichts finden, was dieses Fehlverhalten -wenn es denn eins ist- mir erklären würde. Vlt. übersehe ich aber auch nur einfach was.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Lediglich bei Anwendung von "-O3", macht sich mir ein Fehlverhalten bemerkbar (siehe 9223372036854775808).
Es gibt kein Fehlverhalten. Allenfalls eine auffällige Besonderheit. Von einem Fehlverhalten darfst du sprechen, wenn das Endergebnis nicht stimmt.

Wie schon erwähnt, scheint der Compiler lediglich beim Wert 9223372036854775808 für NumValues, nicht richtig zu optimieren, da die Ausführungszeit Ewigkeiten dauert.
Es gibt kein "richtiges" Optimieren. Kein Standard garantiert dir überhaupt irgendeine Optimierung, geschweige denn, dass solche Abweichungen nicht auftreten.

Alle anderen Werte, sprich bis 9223372036854775807 und ab 9223372036854775809 bis einschließlich dem Maximalwert von unsigned 64-Bit-INT: 18446744073709551615, funktionieren (das Programm wird also in kürzester Zeit in einem Rutsch abgearbeitet).
Ich würde sagen, der Compiler optimiert deine Schleife weg, außer für 0xCCCCCCCCCCCCCCCC.

Hast du mal den generierten Assemblercode verglichen?

Die Frage ist nun, ob das ein Eigenfehler von mir ist oder technisch bedingt bzgl. des Compilers, der Compiler-Option(en) und / oder der verwendeten Bilbiotheken?

Ich tippe auf ein Problem im Optimierer.

was hättet Ihr dann noch für Tipps für mich, wie ich das Problem IRGENDWIE umschiffen kann? Ich muss nämlich definitiv mit solchen Größenordnungen hantieren.
Dein Problem besteht meiner Meinung nach aussschließlich darin, dass der Compiler für einen bestimmten Wert deine Schleife nicht wegoptimieren kann. Sobald dein Code wirklich etwas tut, löst sich das Problem von selbst.
Link zu diesem Kommentar
Auf anderen Seiten teilen

Mal eine ganz doofe Frage: Was passiert bei dem Problemfall wenn du mal das const von NumValues wegläßt?
Hatte ich auch erst gedacht. Aber es würde mich wundern. Habs trotzdem getestet und nein, keine Verbesserung.

Es gibt kein Fehlverhalten. Allenfalls eine auffällige Besonderheit. Von einem Fehlverhalten darfst du sprechen, wenn das Endergebnis nicht stimmt.
Ja, da haste Recht. Ich wusste nicht so recht, wie ich das Phänomen bezeichnen sollte. Aber "Fehlverhalten" ist doch gar nicht mal so abwegig. Ich habe bewusst deshalb das Wort "Fehler" gemieden. Ein Fehlverhalten ist ja schließlich nicht das selbe wie ein Fehler. Aber dieses Verhalten kommt mir irgendwie als Folge eines Fehlers (z.B. Optimierungsfehler) vor. Oder habe ich irgendwas in meinem Code falsch gemacht / falsch angewendet (bezogen auf das Phänomen, nicht im Allgemeinen)?

Es gibt kein "richtiges" Optimieren. Kein Standard garantiert dir überhaupt irgendeine Optimierung, geschweige denn, dass solche Abweichungen nicht auftreten.
Schon klar. Doch in meinem Fall bzw. auf meinem System sollte man doch davon ausgehen können. Immerhin ändert sich ja im Augenblick nichts an meinem System, weder Software, noch Hardware. Also sollte doch der Opcode am Ende der selbe sein.

Ich würde sagen, der Compiler optimiert deine Schleife weg, außer für 0xCCCCCCCCCCCCCCCC.

Hast du mal den generierten Assemblercode verglichen?

Ja, so scheint die Vermutung. Wenn es so ist, wie kann ich es dann erkennen?

Ich meine, ich hab mir schon den Assembler-Output angeschaut, aber so schlau werde ich nicht ganz drauß.

Ich habs jetzt mal mit den zur Verfügung stehenden 128-Bit-Typen getestet. In meinem Beispiel statt uint64_t, also __uint128_t. Und siehe da. Es funktioniert und zwar mit allen Werten des Typs unsigned 64-Bit-INT (uint64_t). Aber so das Gelbe vom Ei ist die Lösung ja nicht. Das "Problem" mit uint64_t bleibt bestehen.

Ich glaube, ich werde das Ganze mal als Bugreport einsenden.

Außer jemand sieht das Problem schon an anderer Stelle bzw. weiß, wie man es richtig machen muss (insofern ich was falsch gemacht habe).

Link zu diesem Kommentar
Auf anderen Seiten teilen

Oder habe ich irgendwas in meinem Code falsch gemacht / falsch angewendet (bezogen auf das Phänomen, nicht im Allgemeinen)?
An deinem Code liegt's nicht.

Doch in meinem Fall bzw. auf meinem System sollte man doch davon ausgehen können.
Nein, sollte man nicht. Standards gibt es nicht ohne Grund. Schau dir die Lizenz deines Compilers an, da steht drin, worauf du dich verlassen kannst. Das kann man grob zu "gar nichts" zusammenfassen. Das Verhalten deines Programms kann sich beim nächsten Compiler-Update ändern. Wenn du deinen Code gezielt auf bestimmte Compilerfeatures hin ausrichtest, solltest du dir über die Folgen im Klaren sein.

Ja, so scheint die Vermutung. Wenn es so ist, wie kann ich es dann erkennen?

Ich meine, ich hab mir schon den Assembler-Output angeschaut, aber so schlau werde ich nicht ganz drauß.

Vermutlich ist für jeden Fall außer 0xCCCCCCCCCCCCCCCC gar keine Schleife drin.

Außer jemand sieht das Problem schon an anderer Stelle bzw. weiß, wie man es richtig machen muss (insofern ich was falsch gemacht habe).

Was du meiner Meinung nach falsch machst, ist dass du dich an einem völlig irrelevanten Problem festbeißt.

Der Sinn deines Tuns wurde hier ja schon mehrfach infrage gestellt. Du testest hier an Optimierungsfeatures herum, deren Anwendbarkeit auf echte Programme ich für sehr begrenzt halte.

Du hast also herausgefunden, dass unter gewissen Umständen sinnlose Schleifen nicht wegoptimiert werden. Was bringt dir diese Erkenntnis? Meiner Meinung nach gar nichts.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...