32bit bzw. 64bit Zahlen



  • int32_t/int64_t ist der richtige Weg.
    Wenn gcc wegen letzterem meckert, kompilierst du vermutlich mit -pedantic-errors. Dieser Parameter muss weg.



  • Das hätten wir dir auch gleich sagen können.

    Ja ... hinterher ist man immer schlauer 🙂

    Mach doch ein menschenlesbares Format ➡ Alle Probleme gelöst.

    Ja ... ääh ... nein ... denn dann ist 1. der Speicherverbrauch ein bissel zu groß (ich produziere teilweise Gigabyte weise Daten ... das liest dann eh keiner mehr manuell durch) und 2. benötige ich die volle Präzision der Ausgabe damit ich die Rechnung später bei Bedarf fortsetzen kann.

    Narf. sizeof gibt die Größe in char-Größen an, nicht in Bit.

    Stimmt natürlich ... Details ...

    Dazu benutzt man in der Regel Makefiles & Co oder das was du als nächstes nennst

    Äh ... ja ... keine Ahnung ... deswegen frage ich ja 😃 Wie kann ich das denn in den Makefiles bestimmen?

    Der gcc4.5 kann anscheinend nativ mit int32_t und int64_t umgehen. Ich müsste dann aber bei allen wichtigen Deklarationen das ändern ... mühsaaaaam ...

    Als Alternative für menschenlesbares Format: Es muss ja nicht direkt menschenlesbar sein, du kannst auch Binärdaten speichern. Aber eben nicht als direktes Speicherabbild wie jetzt. Da kannst du dir neben der Länge noch viel mehr Probleme einhandeln (Endianess, Darstellung negativer Zahlen,...). Gib Zahlen in einem Stellenwertsystem zur Basis 255 (maximum von unsigned char) aus. Das kombiniert binäre Kürze mit Portabilität (ist aber nicht mehr menschenlesbar). Der Nachteil ist, dass Ein- und ausgabe mehr CPU-Last beanspruchen, was in der Praxis aber a) keinen Unterschied macht (Festplatte ist Flaschenhals) und b) selbst wenn doch ein Unterschied da ist, egal ist (weil Ein-/Ausgabe in der Regel nur einen Bruchteil der Programmlaufzeit ausmachen).

    Kannst du das bitte etwas näher ausführen? Wie gebe ich die Daten zur Basis 255 aus?


  • Mod

    harrykane schrieb:

    Kannst du das bitte etwas näher ausführen? Wie gebe ich die Daten zur Basis 255 aus?

    Dies hier ist nur ein schneller Hack zu Demonstrationszwecken. Der Einfachheit halber nur mit vorzeichenlosen Ganzzahlen. Falls dies das richtige für dich ist, kann man sich noch mal über Vorzeichen und Fließkommazahlen unterhalten. Der folgende Code nimmt Zahlen und schreibt und liest sie portabel (einzige Voraussetzung: Die Größe eines Bytes ist 8 Bit. Aber ich denke, dies dürftekeine große Einschränkung sein). Das nette an dem Code ist, dass du nicht die ganzen Variablendeklarationen ändern musst, bloß die Ein-/Ausgabe. Die Zahlen dürfen hier jedoch nicht größer als 2 hoch 32 sein. Das heißt deine Zahlen dürfen zwar 64 Bit lang sein, aber nur den 32-Bit Wertebereich benutzen. Ich vermute mal, dass dies hier ohnehin vorliegt, denn sonst würde dein Programm gar nicht portabel laufen.

    #include <iostream>
    #include <fstream>
    #include <cstdlib>
    
    using namespace std;
    
    long power_255(unsigned int p)
    {
      long result = 1; 
      for (unsigned int i=0; i<p; ++i)
        result *= 255;
    
      return result;
    }
    
    void more_portable_write(unsigned long n, std::ostream &out) // sizeof(long) ist mindesten 32 Bit laut Standard
    {
      for (unsigned int i = 4; i > 0; --i)  // Basenumrechnung wie in der Schule
        {
          ldiv_t result = ldiv(n, power_255(i-1));
          out.put(result.quot);
          n = result.rem;
        }
    }
    
    unsigned long more_portable_read(std::istream &in)
    {
      unsigned long n=0;
      for (unsigned int i = 4; i > 0; --i) // Basenumrechnung rückwärts
        {
          unsigned char ziffer = in.get();
          n += power_255(i-1) * ziffer;
        }  
      return n;
    }
    
    int main()
    {
      // Schreiben
      {
        unsigned int test[]={0,1,50,12345,1234567890,987654321};
        ofstream out("test.dat");
        for(int i=0; i<6; ++i)
          {
            more_portable_write(test[i], out);
          }
      }
    
      // Lesen
      {
        unsigned int test[6]={};
        ifstream in("test.dat");
        for(int i=0; i<6; ++i)
          {
            test[i] = more_portable_read(in);
            cout << i+1 << ". gelesener Wert: "<<test[i]<<endl;
          }
      }
    }
    

    Die erzeugte Binärdatei sieht im Hexeditor so aus:

    00 00 00 00 00 00 00 01 00 00 00 32 00 00 30 69 4A 74 0C B4 3B 8F D6 33
    

    24 Bytes, wie man es von 6 32-Bit Zahlen erwartet, aber überall lesbar.

    Das ist wie gesagt ein schneller Hack, so völlig ohne Sicherheits- und Konsistenzprüfungen würde ich das nicht einsetzen. aber es gibt dir ein Gefühl dafür was ich meine.



  • Um Himmels willen, warum denn Basis 255 und nicht 256?
    Mit Basis 256 hat man keine Performanzprobleme und der Wertebereich wird auch nicht künstlich eingeschränkt.


  • Mod

    Athar schrieb:

    Um Himmels willen, warum denn Basis 255 und nicht 256?
    Mit Basis 256 hat man keine Performanzprobleme und der Wertebereich wird auch nicht künstlich eingeschränkt.

    Ähh, weil ich mich verrechnet habe :p . Ja, 256 sollte es natürlich sein. Entsprechend natürlich auch in dem Beispielprogramm.



  • harrykane schrieb:

    Damit mir das nicht wieder passiert soll in der neuen Version die Länge festgelegt sein ... zB. auf 32bit (64bit). Doch wie mache ich das am elegantesten?

    Du könntest immer ein 32-Bit Programm bauen, dann sind die int immer gleich lang. Da du wohl gcc verwendest: -m32 angeben. Es müssen natürlich die 32Bit-Version des gcc und alle benötigten Bibliotheken installiert werden.

    Lars



  • // Basenumrechnung wie in der Schule

    Ja ... da war mal was ... lang lang ist's her ...

    Aber das ganze 'auf Basis 256 umrechnen' funktioniert doch nur für ganze Zahlen. Für Fließkommazahlen sehe ich es jetzt nicht, wie man das übertragen kann.

    // sizeof(long) ist mindesten 32 Bit laut Standard

    Mal ne blöde Frage ... nur zur Sicherheit: ein "int" ist immer 32bit lang, egal ob nun 32bit oder 64bit System, richtig? "long" ist jedoch Systemabhängig. Wie sieht es dann mit "long long" aus?

    Mich dünkt ich hätte mal irgendwo gelesen, dass "int" auch mal nur 16bit lang war.

    Und wenn wir gerade dabei sind, was ist mit "float" und "double"? Sind die immer 32bit bzw. 64bit lang?

    Gruß,
    HarryKane


  • Mod

    harrykane schrieb:

    // Basenumrechnung wie in der Schule

    Ja ... da war mal was ... lang lang ist's her ...

    Aber das ganze 'auf Basis 256 umrechnen' funktioniert doch nur für ganze Zahlen. Für Fließkommazahlen sehe ich es jetzt nicht, wie man das übertragen kann.

    0,5 = 5*10^(-1) wobei hier mit ^ "hoch" gemeint sein soll. Aber es bietet sich eher an, bei Fließkommazahlen Mantisse und Exponent getrennt zu speichern.

    // sizeof(long) ist mindesten 32 Bit laut Standard

    Mal ne blöde Frage ... nur zur Sicherheit: ein "int" ist immer 32bit lang, egal ob nun 32bit oder 64bit System, richtig? "long" ist jedoch Systemabhängig. Wie sieht es dann mit "long long" aus?

    Alles für signed und unsigned gleichermaßen:
    char: Mindestens 8 Bit (in der Regel auch 😎
    short: Mindestens 16 (sind in der Regel auch 16)
    int: Mindestens 16 (sind in der Regel meist 32)
    long: Mindestens 32 (typischerweise 32 bei 32-Bit Code, 64 bei 64-Bit Code)
    long long (kein Standard C++): Mindestens 64 Bit nach C99, ist auch typischer Wert

    Mich dünkt ich hätte mal irgendwo gelesen, dass "int" auch mal nur 16bit lang war.

    Richtig. Siehe oben. In der Praxis aber nur bei 16-Bit Code (d.h. Code von vor ca. 1995) der Fall.

    Und wenn wir gerade dabei sind, was ist mit "float" und "double"? Sind die immer 32bit bzw. 64bit lang?

    In C++ gar nicht definiert (außer das Wertebereich float<=double<=long double), in C99 gibt es wiederum Mindestwerte, die auch für die meisten C++-Compiler gelten (müssen sie aber nicht!), aber das kannst du dir auch selbst zusammensuchen (Stichwort: float.h).
    Aber typische Werte sind 32, 64 und 64/128 Bit für float, double und long double.



  • [quote="harrykane"]

    // sizeof(long) ist mindesten 32 Bit laut Standard

    Mal ne blöde Frage ... nur zur Sicherheit: ein "int" ist immer 32bit lang, egal ob nun 32bit oder 64bit System, richtig? "long" ist jedoch Systemabhängig. Wie sieht es dann mit "long long" aus?

    Mich dünkt ich hätte mal irgendwo gelesen, dass "int" auch mal nur 16bit lang war.

    Und wenn wir gerade dabei sind, was ist mit "float" und "double"? Sind die immer 32bit bzw. 64bit lang?

    Gruß,
    HarryKane

    Dir ist aber klar, dass du, wenn du wirklich kompatibel sein willst, nicht nur die Länge sondern auch die Byteorder berücksichtigen musst?

    Lars



  • Die Länge der Datentypen war schon immer compilerabhängig und nie einheitlich genormt. Der jeweils gültige Wertebereich sollte in der Doku des Compilers klar beschrieben sein. Die Längenangaben in bit oder byte haben sich nicht durchgesetzt. In C/C++ bleiben unsigned, short, double, long double oder ohne Zusatz bequemer. Was früher 8- oder 16-bit war kann heute 32- oder 64-bit sein, morgen vielleicht 128-bit. Der Speicherbedarf spielt nur noch eine untergordnete Rolle. Man hat ja genug Speicher und die Performance regeln die immer schneller werdenden Prozessoren!

    Die Frage ist also nur für Optimierungen interessant oder falls einmal die Grenzen der Wertebereiche tatsächlich nicht ausreichen.


Anmelden zum Antworten