Fragen zu 128-Bit Integer



  • Ein "compound literal" ist ein Literal für Compound-Typen, also Structs und Arrays. Beispielsweise kann man in C99 Folgendes schreiben:

    struct point {
      double x;
      double y;
    };
    
    void foo(struct point p) {
      // ...
    }
    
    // ...
    
    foo((struct point) { 0.0, 0.0 });
    

    In meinem Beispiel geht es dabei um das (char[64]){0} in

    #define prn_u128(x) prn_u128_aux((char[64]){0}, x)
    

    ...womit quasi inline ein Buffer angelegt wird, der groß genug ist, um die Dezimaldarstellung eines 128-Bit-Integers aufzunehmen und lange genug vorhanden ist, um danach ausgegeben zu werden. Die Funktionen prn_i128_aux und prn_u128_aux geben einen Zeiger auf diesen Buffer zurück.

    Was dynamische Buffer angeht: Die grundlegende Funktionalität kann man mit allen Arten von Buffern umsetzen, solange sie groß genug sind, aber ich sehe keine Vorteile darin, das hier dynamisch anzugehen. Dynamische Speicheranforderung ist generell langsamer, als einfach ein bisschen Kram auf den Stack zu legen, und man muss den Speicher hinterher explizit wieder freigeben. Das mögen keine riesigen Probleme sein, aber ich sehe keinen Vorteil, der es rechtfertigte, sich diese Umstände einzuhandeln.



  • Und nochmal ich. 🙄
    Hab jetzt ne funktionierende C-Lösung, welche ich einfach mal prn_int128_2() genannt hab. Ich finde sie aber noch nicht ausgereift.
    Wäre schön, wenn da mal jemand drüberschauen könnte:

    #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 <fstream>
    #include <sstream>
    #include <termios.h>
    #include <cmath>
    #include <vector>
    #include <array>
    
    using namespace std;
    
    #define free(x) free(x); *x=NULL
    #define PRId128 "s"
    #define PRIi128 "s"
    #define PRIu128 "s"
    
    __int128_t INT128_MIN = (-170141183460469231731.687303715884105728) * pow(10,18);
    __int128_t INT128_MAX = ( 170141183460469231731.687303715884105727) * pow(10,18);
    
    const char* prn_int128_2(__int128_t x)
    {
      char *p=(char *)malloc(sizeof(char)*41);
      bool sign = false;
      bool isINT128_MIN = false;
    
      if(x == 0)
      {
        p[0] = '0';
        p[1] = '\0';
        return p;
      }
      if(x < 0)
      {
        sign = true;
        if(x == INT128_MIN)
        {
          ++x;
          isINT128_MIN = true;
        }
        x *= -1;
      }
      int i = 0;
      for(;x > 0;++i)
      {
        if(x < 10)
        {
          p[i] = '0' + (char)(x);
          x = 0;
        }
        else
        {
          if(isINT128_MIN == true)
          {
            p[i] = '8';
            isINT128_MIN = false;
          }
          else
          {
            p[i] = '0' + (char)(x % 10);
          }
          x /= 10;
        }
      }
      p[i]='\0';
    
      char buffertmp[41]={NULL};
      for(int j=0;p[j]!='\0';++j)
      {
        buffertmp[j]=p[i-j-1];
      }
      for(int k=0;buffertmp[k]!='\0';++k)
      {
        p[k]=buffertmp[k];
      }
      if(sign == true)
      {
        for(int l=0;l<40;++l)
        {
          p[i-l+1] = p[i-l];
        }
        p[0] = '-';
      }
      return p;
    }
    
    int main(int argc, char *argv[])
    {
      cout << "\n+Mit printf() UND prn_128_2():\n----------------------------------------------------\n";
      printf("INT128_MIN:%"PRIi128"\n",prn_int128_2(INT128_MIN));    //geht
      printf("INT128_MAX: %"PRIi128"\n",prn_int128_2(INT128_MAX));   //geht
      printf("INT128:     %"PRIi128"\n",prn_int128_2(0));            //geht
      printf("INT128:     %"PRIi128"\n",prn_int128_2(1024));         //geht
      printf("INT128:    %"PRIi128"\n",prn_int128_2(-1024));         //geht
      //geht:
      printf("INT128_MIN:%"PRIi128"\nINT128_MAX: %"PRIi128"\nINT128:     %"PRIi128"\n",prn_int128_2(INT128_MIN),prn_int128_2(INT128_MAX),prn_int128_2(1024));
    
      getchar();
      return 0;
    }
    

    Output (Exec):

    +Mit printf() UND prn_128_2():
    ----------------------------------------------------
    INT128_MIN:-170141183460469231731687303715884105728
    INT128_MAX: 170141183460469231731687303715884105727
    INT128:     0
    INT128:     1024
    INT128:    -1024
    INT128_MIN:-170141183460469231731687303715884105728
    INT128_MAX: 170141183460469231731687303715884105727
    INT128:     1024
    

    Wie Ihr seht, es funktioniert tadellos. Sowohl Zwischenwerte, wie: 0, 1024, -1024 als auch die Grenzwerte von 128-Bit-Signed-Integer.
    Ebenso kann man nun die Funktion einzeln oder auch mehrfach in printf() verwenden.

    Was mich nun konkret stört:
    - sehr viele Schleifen und IF/ELSE. (wo könnte man da vieles vereinfachen?)
    - char-Array p wird dynamisch erzeugt (wie bringt man da free() nun mit ein?)
    - weitere Optimierungen & Kritiken sind gerne willkommen

    Grüße

    Schlitzauge 🙂



  • Haha. Das ist nicht dein Ernst, oder?



  • ???
    Naja, es funktioniert und ich sagte doch auch, dass ich die Lösung noch nicht perfekt finde. Ich wäre natürlich lieber an einer statischen Lösung interessiert, aber die einzige statisch funktionierende Lösung hier, setzt GCC/g++ mit "-std=c99" voraus. Ich verwende aber "-std=c++0x" und möchte auch dabei bleiben.



  • Du leckst Speicher wie ein Küchensieb; ich würde das nicht als "funktioniert tadellos" bezeichnen.

    Die Frage ist ein bisschen, was du eigentlich willst. Die grundlegende Funktionalität lässt sich auf verschiedene Arten umsetzen; schwierig ist allein der syntaktische Zucker. Du wirst printf keine neuen Format-Spezifikatoren beibringen können, und in C89 wirst du einen Aufruf der Form printf("%s\n", prn_i128(x)); auch nicht kriegen. Für C++ und C99 habe ich dir bereits Code gegeben.

    Der dynamische Ansatz ist hier nicht wirklich sinnvoll - wenn du den syntaktischen Zucker aufweichst und einen weiteren Parameter mitgibst, um den Zeiger aufzunehmen und nachher freizugeben, kannst du genau so gut einen Zeiger auf einen Buffer auf dem Stack mitgeben. Mehr als das ist wohl nicht drin.



  • Deine Lösung ist ja auch nicht schlecht, ganz im Gegenteil. Aber sie lässt sich bei mir mit "-std=c++0x" einfach nicht übersetzen und zweigleißig was Compiler- sowie Compiler-Optionen betrifft, wollte ich dann doch nicht fahren.
    Wenn Du mir noch sagen könntest, wie man Deine letzte Lösung auch im C++0x-Mode übersetzen und nutzen könnte, wärs ja perfekt.

    Ebenso wüsste ich gerne, wie ich meine Lösung statisch gestalten kann, ohne das man für jeden Aufruf ein extra char-Array benötigt.

    Mit "funktioniert tadellos" bezog ich mich ausschließlich auf die Ausgabe und die funktioniert nunmal tadellos, egal welche Werte ich mitgebe. Ich würde nur gerne die dynamisch erzeugten char-Array´s (p´s) wieder freigeben lassen. Nur wie?



  • Komm von printf weg, oder lebe mit den Einschränkungen.



  • 314159265358979 schrieb:

    Komm von printf weg, oder lebe mit den Einschränkungen.

    Wie oft denn noch? Habe ich einen C++-fähigen Compiler, nehme ich std::cout, keine Frage.

    std::cout oder andere C++-Elemente funktionieren nunmal aber nicht bei reiner C-Anwendung.

    Wie dem auch sei. Wenn jemand noch weiter weiß, kann er/sie es ja gerne hier posten. Ebenso wäre ich sehr dankbar für Vereinfachungen und Optimierungen meiner dynamisch gestalteten Lösung. Besser eine, als keine.

    Es geht ja auch darum, die Grenzen des "Möglichen" neu zu entdecken.



  • Schau auf Seite 4 dieses Threads, dritter Beitrag von unten; das kriegst du auch durch einen C++-Compiler. Danach halt

    #ifdef __cplusplus
    // C++-Code
    #else
    // C-Code
    #endif
    

    ...und in Dreckiger-Hack-Land ist alles wieder in Butter.

    Dass du C99-Code durch einen C++-Compiler nicht durchjagen kannst, ist nicht weiter verwunderlich - schließlich handelt es sich bei C und C++ um verschiedene Sprachen, und wo C++98 von C89 noch viel übernommen hat, ist das bei C99 nicht der Fall.



  • Schlitzauge schrieb:

    Wie oft denn noch? Habe ich einen C++-fähigen Compiler, nehme ich std::cout, keine Frage.

    Was du nicht sagst.

    Schlitzauge schrieb:

    std::cout oder andere C++-Elemente funktionieren nunmal aber nicht bei reiner C-Anwendung.

    Dann lebe mit den Einschränkungen.

    Schlitzauge schrieb:

    Wie dem auch sei. Wenn jemand noch weiter weiß, kann er/sie es ja gerne hier posten. Ebenso wäre ich sehr dankbar für Vereinfachungen und Optimierungen meiner dynamisch gestalteten Lösung. Besser eine, als keine.

    seldon hat dir bereits geholfen so gut es geht. Einerseits möchtest du eine C-printf Lösung, andererseits soll es mit C++0x kompilierbar sein. Das ergibt keinen Sinn.

    Schlitzauge schrieb:

    Es geht ja auch darum, die Grenzen des "Möglichen" neu zu entdecken.

    Da gibts nix neu zu entdecken, wir können programmieren. Die bestmöglichen Lösungen hast du bekommen, es _geht_ nicht besser.

    Und ganz ehrlich gesagt bezweifle ich bei deinen Kenntnissen auch mal stark, dass du den Typen brauchst.

    @seldon: Selbe Sekunde, nice 😉



  • Es geht ja auch darum, die Grenzen des "Möglichen" neu zu entdecken.

    Wie willst du die Implementation einer Funktion wie printf aendern, wenn du keinen Zugriff auf den Quellcode hast? Ich verstehe dein Anliegen in keinster Weise. Der andere Punkt: Wenn es dir um die Grenzen des Moeglichen geht, dann lote sie selbst aus. Was brauchst du uns dazu?



  • seldon schrieb:

    Schau auf Seite 4 dieses Threads, dritter Beitrag von unten; das kriegst du auch durch einen C++-Compiler.

    An #ifdef __cplusplus hatte ich in der Tat auch schon gedacht, würde ich letztendlich vlt. sogar machen. Das wäre für mich aber erst zweite Wahl gewesen. Ich fand bzw. finde es noch sehr reizvoll, dass codeseitig mit eigener[n] Funktion(en) zu realisieren, denn das scheint ja doch nicht allzu einfach zu sein, wenn es denn möglich sein sollte.
    Aber bzgl. des dritten Beitrags von unten auf Seite 4:
    Warum sollte das auch nicht gehen? Ist doch eine C++-Lösung oder etwa nicht?
    Oder haste Dich einfach in der Seite und/oder Beitrag geirrt?

    314159265358979 schrieb:

    Schlitzauge schrieb:

    Wie oft denn noch? Habe ich einen C++-fähigen Compiler, nehme ich std::cout, keine Frage.

    Was du nicht sagst.

    Doch doch, oft genug. Es ist ja auch kein Ding, einfach std::cout zu verwenden, C++-fähigen Compiler vorausgesetzt, versteht sich.
    Es kommt leider aber auch mal vor, dass ich an PC´s sitze, wo es nur C-Compiler gibt und ich "mit den Einschränkungen nunmal leben muss".

    314159265358979 schrieb:

    Dann lebe mit den Einschränkungen.

    Es ist für mich kein MustHave, sondern reine Interessenfrage. 😉

    314159265358979 schrieb:

    seldon hat dir bereits geholfen so gut es geht. Einerseits möchtest du eine C-printf Lösung, andererseits soll es mit C++0x kompilierbar sein. Das ergibt keinen Sinn.

    Nicht nur er, jeder hier. Deshalb nochmal großen THX an alle.
    Aber Du irrst, es ergibt schon einen Sinn. Bei Lösungen für Beides ist es dann egal, ob man in C++ oder reinem ANSI-C schreibt.
    Zur Not schreibe ich mir halt eine printf_[u]i128()-Funktion, die selbst inline printf() für jedes extrahiertes Zeichen für jeweils eine Ausgabe verwendet. GMP geht ja ähnlich vor.
    Ebenso könnte ich auch die bisherige statische C-Lösung verwenden. Müsste dann halt nur jede __[u]int128_t-Ausgabe mit printf() einzeln tätigen, denn das funktioniert ja, nur mehrfach nicht.
    Und wenn C++-Compiler vorhanden, verwende ich einfach std::cout bei printf(), wenn ich mit C++-Compiler hier und dort ANSI-C verwenden möchte. Lösungen dafür konnte ich breits implementieren.

    314159265358979 schrieb:

    Da gibts nix neu zu entdecken, wir können programmieren. Die bestmöglichen Lösungen hast du bekommen

    Habe auch nie etwas anderes behauptet oder gedacht. Ich respektiere schon Eure(r) Meinungen, Wissen und Können.

    314159265358979 schrieb:

    Und ganz ehrlich gesagt bezweifle ich bei deinen Kenntnissen auch mal stark, dass du den Typen brauchst.

    Das ich natürlich nicht alles kann, ist gewiss, wäre ja auch langweilig schon alles zu wissen bzw. zu können. Wo bleibt da der Spaß und Reiz?
    Betrachtet es halt wie ich: als [kleine] Herausforderung das vermeintlich Unlösbare, doch irgendwie lösbar zu machen (Soll jetzt kein Befehl oder Dergleichen sein. Es steht natürlich jedem frei!).
    Doch Du irrst wieder. Ich brauche sehr wohl solch große Zahlenbereiche solchen Typs, da ich Anwendungen für Astrophysik und Kernphysik schreibe.

    --------------------
    Naja, wie dem auch sei. Ich glaube mit einer C-Lösung unter gegebenen Bedingungen, die ich mir stelle, kommen wir hier wohl nicht mehr weiter. Es sollte ja auch nur was Optionales sein. Hätte trotzdem noch jemand Lösungen / Lösungsvorschläge parat, bitte POSTEN! THX!

    Eines wäre trotzdem noch super. Könnte jemand mal über meine dynamisch gestaltete Lösung schauen. Ich hatte da noch drei Kritikpunkte (Seite 6, letzter Beitrag), u.a. wie ich den allokierten Speicher wieder freigeben könnte (z.B. mit free()) oder auch, was mir wichtiger ist, wie man dort manches vereinfachen könnte.
    Oder könnte man meine Lösung irgendwie auch statisch gestalten? An der Art der Bedienung sollte sich dabei natürlich nichts ändern, wenn möglich.

    Ich wollte mich auch nicht allzu lange damit aufhalten, da ich im Moment eh an einem Rechner mit C++-Compilern sitze, was auch für die nächste Zeit gilt. Außerdem befinden wir uns hier im C++-Abteil, nicht für C.

    Deshalb wieder zu meinem ursprünglichen Anliegen. Einige C++-Lösungen konnte ich erfolgreich implementieren und funktionieren wunderbar. Einige jedoch nicht, da der Compiler etwas auszusetzen hat. Diese Meldungen kann ich allerdings übrhaupt nicht deuten.

    C++-Lösung 1:

    const char* INT128ToCSTR3(__int128_t x,std::vector<char>& buffer)
    {
      std::stringstream sstr;
      sstr<<x;
      buffer[sstr.str().copy(buffer,buffer.size()-1,0)] = 0;
      return &buffer[0];
    }
    
    const char* UINT128ToCSTR3(__uint128_t x,std::vector<char>& buffer)
    {
      std::stringstream sstr;
      sstr<<x;
      buffer[sstr.str().copy(buffer,buffer.size()-1,0)] = 0;
      return &buffer[0];
    }
    

    Compiler-Output zu C++-Lösung 1:

    sizeofs_main.cpp: In function ‘const char* INT128ToCSTR3(__int128_t, std::vector<char, std::allocator<char> >&)’:
    sizeofs_main.cpp:123: error: no matching function for call to ‘std::basic_string<char, std::char_traits<char>, std::allocator<char> >::copy(std::vector<char, std::allocator<char> >&, size_t, int)’
    /usr/lib/gcc/x86_64-redhat-linux/4.4.5/../../../../include/c++/4.4.5/bits/basic_string.h:1592: note: candidates are: typename std::basic_string<_CharT, _Traits, _Alloc>::size_type std::basic_string<_CharT, _Traits, _Alloc>::copy(_CharT*, typename _Alloc::rebind<_CharT>::other::size_type, typename _Alloc::rebind<_CharT>::other::size_type) const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
    sizeofs_main.cpp: In function ‘const char* UINT128ToCSTR3(__uint128_t, std::vector<char, std::allocator<char> >&)’:
    sizeofs_main.cpp:131: error: no matching function for call to ‘std::basic_string<char, std::char_traits<char>, std::allocator<char> >::copy(std::vector<char, std::allocator<char> >&, size_t, int)’
    /usr/lib/gcc/x86_64-redhat-linux/4.4.5/../../../../include/c++/4.4.5/bits/basic_string.h:1592: note: candidates are: typename std::basic_string<_CharT, _Traits, _Alloc>::size_type std::basic_string<_CharT, _Traits, _Alloc>::copy(_CharT*, typename _Alloc::rebind<_CharT>::other::size_type, typename _Alloc::rebind<_CharT>::other::size_type) const [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
    

    Das wäre die eine gewesen. Jetzt noch die zweite:

    C++-Lösung 2:

    template<std::size_t BuffSize>
    const char* INT128ToCSTR4(__int128_t x,std::array<char,BuffSize>& buffer)
    {
      std::stringstream sstr;
      sstr<<x;
      buffer[sstr.str().copy(buffer,buffer.size()-1,0)] = 0;
      return &buffer[0];
    }
    
    template<std::size_t BuffSize>
    const char* UINT128ToCSTR4(__uint128_t x,std::array<char,BuffSize>& buffer)
    {
      std::stringstream sstr;
      sstr<<x;
      buffer[sstr.str().copy(buffer,buffer.size()-1,0)] = 0;
      return &buffer[0];
    }
    

    Übersetzen lässt sie sich. Ich weiß nur nicht so recht, wie man diese dann anwendet.

    Folgendes scheint anscheinend nicht zu funktionieren:

    //char buffer3[41] = {NULL};
      std::array<char,41> buffer3/* = {NULL}*/;
      printf("%s\n",INT128ToCSTR4(2048,buffer3));
    

    Auch nicht, wenn ich ein klassisches Array verwende (Zeile 1).
    Nebenbei: wie kann ich alle Elemente eines std::array´s mit NULL initialisieren?

    Grüße

    Schlitzauge 🙂

    EDIT:

    knivil schrieb:

    Wie willst du die Implementation einer Funktion wie printf aendern, wenn du keinen Zugriff auf den Quellcode hast? Ich verstehe dein Anliegen in keinster Weise. Der andere Punkt: Wenn es dir um die Grenzen des Moeglichen geht, dann lote sie selbst aus. Was brauchst du uns dazu?

    Nein nein. printf() möchte ich so garnicht neu implementieren. Hab mich damit abgefunden, dass printf() keine neuen Typen direkt schluckt, sondern dass man dazu ne Funktion drumherumschreiben muss, welche anschließend ein printf()-fähiges Format liefert. Ich bin schon seit Wochen dabei "die Grenzen" selber auszuloten. Doch a.) weiß und kann ich nicht alles, b.) mehrere Augen sehen bzw. können mehr und c.) COMMUNITY-Gedanke halt (ist schließlich ein Forum).



  • Schlitzauge schrieb:

    Doch Du irrst wieder. Ich brauche sehr wohl solch große Zahlenbereiche solchen Typs, da ich Anwendungen für Astrophysik und Kernphysik schreibe.

    Mit int64_t kann ich Zahlen bis ±9223372036854775807 speichern. Mit double kann ich auf keine Ahnung wie viele Kommastellen genau Zahlen speichern, die wohl bis 10e300 oder so rauf gehen. Ich kann mir nicht vorstellen, dass das nicht ausreicht. Wenns wirklich so ist, kann man bestimmt was am Algortihmus umstellen, und wenns dann immer noch nicht reicht, dann nimm eine Library wie GMP dafür.



  • Naja, wenn man sich im Bereich der Astrophysik begibt - es gibt schwarze Löcher mit einer Masse von 1040 kg. Ob man die jetzt aufs Gramm genau braucht (oder kriegt) ist natürlich eine andere Frage.

    Double arbeitet mit einer Mantisse von 53 binären Stellen, was knapp 16 Dezimalstellen bedeutet (log10(253) ≈ 15,95), aber je nach Anwendungsfall kann es durchaus sein, dass Fließkommaberechnung weit weniger genaue Ergebnisse liefert - Differenzen zwischen sehr ähnlichen Werten, wie sie in der Elementarphysik durchaus auftreten können, säbeln dir, wenn du nicht aufpasst (und oft auch dann) viele Stellen Genauigkeit einfach weg. Es ist schon vorstellbar, dass man mit Fixed-Point-Arithmetik auf einem 128-Bit-Integer besser dran sein kann. Ggf. würde ich mich aber mit MP-Bibliotheken vertraut machen, denn wenn ich damit rechnen muss, einen 128-Bit-Integer auszureizen, würde ich auch damit rechnen, noch darüber hinaus gehen zu müssen.



  • Gibt natürlich auch noch long double, da sollten sich noch ein paar Stellen gewinnen lassen.



  • struct int_buffer
    {
       char arr[64];
       char *p;
    };
    
    #define int128(x) int128_2_str(x).p
    
    int_buffer int128_2_str(__int128_t x)
    {
       struct int_buffer buffer; /* muss man das in C noch so schreiben ? */
       buffer.p = buffer.arr + 63; 
       *(buffer.p) = '\0';
       --(buffer.p);
       /* jetzt arbeitest du mit dem buffer.p weiter */
    
       /* hier kommt dein code, wie gehabt */
    
       return buffer;
    }
    
    printf("%s", int128(123123232123321));
    

    damit sollte das buffer problem geloest sein

    Meep Meep


  • Mod

    Meep Meep schrieb:

    damit sollte das buffer problem geloest sein

    Das hatten wir schon. Und wenn das eine Lösung für C sein soll: das ist undefiniert. C kennt keine temporärem Objekte.



  • camper schrieb:

    Und wenn das eine Lösung für C sein soll: das ist undefiniert. C kennt keine temporärem Objekte.

    oh. das wusste ich nicht

    das das bufferproblem geloest wurde, hab ich scheinbar ueberlesen


Anmelden zum Antworten