Fragen zu 128-Bit Integer


  • Mod

    Meep Meep schrieb:

    habs der einfachhaltshalber mit nem int gemacht

    und vermutlich auch nicht getestet. Eine Ellipse führt sicher nicht dazu, dass ein Konvertierungsoperator aufgerufen wird.



  • camper schrieb:

    Meep Meep schrieb:

    habs der einfachhaltshalber mit nem int gemacht

    und vermutlich auch nicht getestet. Eine Ellipse führt sicher nicht dazu, dass ein Konvertierungsoperator aufgerufen wird.

    wie ? was ? wo ?
    welche ellipse ?


  • Mod

    printf("test: "PRIi128" -> "PRIi128"\n", i128(12), i128(24));
    
    int printf(const char* format, ...);
    


  • @camper:
    Die Ellipse nicht, aber dafür sollte doch das grauenhafte i128 Makro sorgen.
    Da steht dann im Endeffekt ja

    printf("test: %s -> %s\n", (const char*)myint(12), (const char*)myint(24));
    

    Abgesehen davon dass ich es grässlich finde... funktionieren müsste das IMO schon.



  • das PRIi128 hatte ich nur eingefuegt weil Schlitzauge sowas haben wollt 🙂



  • Sers,

    Jo, GROßEN DANK an alle. Die Lösungsvorschläge helfen mir sehr.
    Hab jetzt einige INT128ToCSTR-Funktionen erfolgreich implementiert.
    Bei drei Implementierungen komme ich allerdings nicht weiter.
    Ich möchte jetzt auch nicht diese drei parallel diskutieren, Zwecks Übersicht.
    Deshalb eine nach der anderen.

    Alle bisherigen Implementierungen, verwenden C++-spezifische Elemente.
    Lediglich der Output ist dann für printf() geeignet (was jetzt nicht weiter schlimm ist, insofern ich C++-fähige Compiler einsetze; => dann würde ich sowieso C++ bevorzugt coden).
    Es wurde aber auch eine reine C-Implementierung vorgeschlagen:

    const char* prn_int(int x)
    {
       static const int size = 20;
       static char buf[size];
       char *p = buf + size - 1;
       *p = '\0';
       --p;
       bool sign = false;
    
       if(x == 0)
       {
          *p = '0';
          return p;
       }
    
       if(x < 0)
       {
          sign = true;
          x *= -1;
       }
    
       while(x > 0)
       {
          if(x < 10)
          {
             *p = '0' + static_cast<char>(x);
             x = 0;
          }
          else
          {
             *p = '0' + static_cast<char>(x % 10);
             x /= 10;
          }
    
          --p;
       }
    
       if(sign == true)
          *p = '-';
       else
          ++p;
    
       return p;
    }
    

    Ich wollte jetzt gerne diese Funktion ebenso mit OUT-Parameter (also mittels übergebenen C-Array) realisieren, komme da aber irgendwie nicht weiter. Soll heißen, es funktioniert nicht.
    Wie würde denn eine richtige Implementierung aussehen? (wie gesagt, geht es hier um reines ANSI-C, kein C++)

    Grüße

    Schlitzauge 🙂



  • // size = buffergroesse
    const char* prn_int(int x, char *buf, int size)
    {
       char *p = buf + size - 1;
       *p = '\0';
       --p;
       bool sign = false;
    
       if(x == 0)
       {
          *p = '0';
          return p;
       }
    
       if(x < 0)
       {
          sign = true;
          x *= -1;
       }
    
       while(x > 0)
       {
          if(x < 10)
          {
             *p = '0' + static_cast<char>(x);
             x = 0;
          }
          else
          {
             *p = '0' + static_cast<char>(x % 10);
             x /= 10;
          }
    
          --p;
       }
    
       if(sign == true)
          *p = '-';
       else
          ++p;
    
       return p;
    }
    

    du musst natuerlich dafuer sorge tragen das dein buffer gross genug ist

    Meep Meep



  • Ich hab den Code mal korrigiert, denn bei INT128_MIN kommt sonst "-0" heraus.

    Das sind die genauen 128-Bit-Grenzwerte:

    INT128_MIN: -170141183460469231731687303715884105728
    INT128_MAX:  170141183460469231731687303715884105727
    

    Wenn jetzt nach obigen Code x*=(-1) gerechnet wird, würde die Grenze von INT128_MAX gesprengt werden, wenn man der Funktion INT128_MIN übergeben würde.

    Hier meine verbesserte Version:

    const char* prn_int128(__int128_t x, char *buf, int size)
    {
      char *p = buf + size - 1;
      *p = '\0';
      --p;
      bool sign = false;
      bool isINT128_MIN = false;
    
      if(x == 0)
      {
        *p = '0';
        return p;
      }
      if(x < 0)
      {
        sign = true;
        if(x == INT128_MIN)
        {
          ++x;
          isINT128_MIN = true;
        }
        x *= -1;
      }
      while(x > 0)
      {
        if(x < 10)
        {
          *p = '0' + /*static_cast<char>*/(char)(x);
          x = 0;
        }
        else
        {
          if(isINT128_MIN == true)
          {
            *p = '8';
            isINT128_MIN = false;
          }
          else
          {
            *p = '0' + /*static_cast<char>*/(char)(x % 10);
          }
          x /= 10;
        }
        --p;
      }
    
      if(sign == true)
      {
        *p = '-';
      }
      else
      {
        ++p;
      }
      return p;
    }
    

    Die Korrektur gewährleistet, dass ausschließlich im Falle von x==INT128_MIN, die erste Ziffer als 8 gesetzt wird und zwar nur im Output.

    Jetzt habe ich die alte und neue Version mal getestet und es funktioniert.
    Allerdings gibt es nach wie vor Probleme mit der Funktion:

    char testbuffer[41]={NULL};
      cout << "\n+Mit printf() UND prn_128():\n----------------------------------------------------\n";
      printf("INT128_MIN: %s\n",prn_int128(INT128_MIN,testbuffer,41));     //geht
      printf("INT128_MAX:  %s\n",prn_int128(INT128_MAX,testbuffer,41));    //geht
      printf("INT128:      %s\n",prn_int128(0,testbuffer,41));             //geht
      printf("INT128:      %s\n",prn_int128(1024,testbuffer,41));          //geht
      printf("INT128:     %s\n\n",prn_int128(-1024,testbuffer,41));        //geht
      //geht nicht:
      printf("INT128_MIN: %s\nINT128_MAX:  %s\nINT128:      %s\n",prn_int128(INT128_MIN,testbuffer,41),prn_int128(INT128_MAX,testbuffer,41),prn_int128(1024,testbuffer,41));
    

    Output:

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

    Wie Ihr sehen könnt, funktionierts einzeln hervorragend, sowohl mit den Grenzwerten von 128-Bit signed Integer als auch mit sonstigen Werten zwischendrinn (Output: Zeilen 3-7).

    Sobald man die Funktion aber mehrfach in printf() verwenden möchte (Output: Zeilen 9-11), stimmt lediglich die erste Ausgabe, die darauffolgenden nicht mehr.
    Ich verwende zwar bei jedem der drei Funktionsaufrufe das gleiche char-Array, lasse dafür aber auch unterschiedliche Werte übergeben.

    Ich hab mir jetzt die Funktion schon etliche Male durchgeschaut und auch den Debugger mehrfach bemüht, sehe aber nicht, wo der Fehler liegt und wie ich es besser machen müsste. Wäre sehr dankbar für jeden Tipp bzw. Lösungsvorschlag.

    Grüße

    Schlitzauge 🙂


  • Mod

    Schlitzauge schrieb:

    Ich verwende zwar bei jedem der drei Funktionsaufrufe das gleiche char-Array,

    Das.

    hustbaer schrieb:

    @camper:
    Die Ellipse nicht, aber dafür sollte doch das grauenhafte i128 Makro sorgen.
    Da steht dann im Endeffekt ja

    printf("test: %s -> %s\n", (const char*)myint(12), (const char*)myint(24));
    

    Abgesehen davon dass ich es grässlich finde... funktionieren müsste das IMO schon.

    Ok, hätte nicht bloß quer lesen sollen... da der Bezeichner klein geschrieben wird, habe ich nicht an ein Makro gedacht.



  • Was wie was DAS?
    Verstehe nicht so recht.

    Meine Vermutung liegt zwar auch bei der Verwendung von nur einem char-Array, aber ehrlich gesagt möchte ich nicht für jeden printf()-Wert ein extra Array anlegen müssen. Ich verstehe nicht, warum es mit einem Array nicht funktionieren sollte.
    Wie genau geht printf() bei der Interpretation denn vor?

    Hätte denn wer nen Lösungsvorschlag, wie ich es auch mit einem char-Array lösen könnte? Ich bin langsam mit meinem Latein am Ende.
    Wie gesagt, ausschließlich bzgl. dieser C-Funktion. Lösungen und Implementierungen mit C++-Elementen habe ich schon zu Genüge (herzlichen THX nochmal! 🙂 ).

    Grüße

    Schlitzauge 🙂



  • Meine Vermutung liegt zwar auch bei der Verwendung von nur einem char-Array, aber ehrlich gesagt möchte ich nicht für jeden printf()-Wert ein extra Array anlegen müssen. Ich verstehe nicht, warum es mit einem Array nicht funktionieren sollte.
    Wie genau geht printf() bei der Interpretation denn vor?

    Zuerst werden alle 3 Funktionen aufgerufen, dann bekommt printf die 3 Ergebnisse als Parameter überreicht.
    Alle nutzen den selben Buffer, also zeigen alle 3 auf den Text, in dem das Ergebnis der letzten Berechnung steht.



  • Also doch. Habs zwar vermutet, konnte es aber nicht so recht glauben.
    Hat diese Vorgehensweise der Interpretation einen Vorteil?

    Um mal beim Punkt zu bleiben: auch wenn erst alle drei Funktionen ausgeführt werden (was logisch ist), liefert jeder Funktionsaufruf ein separates "return" und auch die übergebenden und zu verarbeitendenen Werte sind unterschiedlich.
    Alle drei Funktionsaufrufe liefern also die selbe Adresse des char-Arrays in die "%s"-Makros/-bzw. Formatstrings?

    Logisch wäre dieses Verhalten, wenn alle drei Funktionsaufrufe parallel ausgeführt werden würden und jede Funktion zur selben Zeit in das char-Array schreiben wollte.
    Aber werden die drei Funktionsaufrufe von prn_int128() in printf() nicht seriell, also nacheinander ausgeführt? Dann dürfte es doch egal sein, ob alle drei Funktionsaufrufe sich dem gleichen char-Array bedienen.

    Selbst wenn printf() erst nach Ausführung der drei Funktionsaufrufe das char-Array an "%s" übergibt bzw. diese anstelle dessen ersetzt, erklärt dies nicht, weshalb der Output aller drei Funktionsaufrufe in printf() dann unterschiedlich aussieht? Müsste wenn schon, nicht dann der Output jedesmal der Gleiche sein?



  • Das Array innerhalb der Funktion ist statisch, belegt also bei jedem Funktionsaufruf exakt denselben Speicher. Du rufst dreimal nacheinander die Funktion auf, die die Zahl in das Array schreibt und dessen Adresse zurückgibt. Dann rufst Du die printf-Funktion auf, die dreimal dieselbe Adresse übergeben bekommt (nämlich die von den drei Funktionsaufrufen zurückgegebene).

    Spätestens an diesem Punkt müsste Dir klar werden, dass es technisch unmöglich ist, dass an dieser Adresse drei unterschiedliche Strings stehen.



  • Müsste die Ausgabe allerdings dann nicht immer die gleiche bleiben?



  • Entschuldige, ich war noch nicht bei der letzten Funktion, sondern bei der vorherigen (mit static char buf ganz oben).

    Hier ist das Problem, dass die Funktion ja nicht die Anfangsadresse von testbuffer zurückgibt, sondern die Adresse des zuletzt geschriebenen Zeichens. Da 1024 zur vier Stellen lang ist, ist das die Adresse von testbuffer[36]. In testbuffer steht nach den drei Aufrufen von prn_int128 offenbar INT128_MIN (da die Aufrufreihenfolge nicht definiert ist). Und jetzt vergleiche mal die letzten vier Stellen von INT128_MIN mit Deiner Ausgabe 🙂



  • Du wirst schon getrennte Speicherbereiche zur Verfügung stellen müssen. In C99 könnte man sich wohl mit compound literals behelfen. Mal eben hingekladdet:

    #include <stdio.h>
    
    char *prn_u128_aux_aux(char *dest, __uint128_t x) {
      char *p = dest;
    
      if(x >= 10) {
        p = prn_u128_aux_aux(dest, x / 10) + 1;
      }
    
      *p = '0' + x % 10;
      return p;
    }
    
    char *prn_u128_aux(char *dest, __uint128_t x) {
      prn_u128_aux_aux(dest, x);
      return dest;
    }
    
    char *prn_i128_aux(char *dest, __int128_t x) {
      if(x < 0) {
        *dest = '-';
        prn_u128_aux(dest + 1, -x);
      } else {
        prn_u128_aux(dest, x);
      }
    
      return dest;
    }
    
    // Die Speicherbereiche werden hier intern beim Aufruf angelegt
    #define prn_u128(x) prn_u128_aux((char[64]){0}, x)
    #define prn_i128(x) prn_i128_aux((char[64]){0}, x)
    
    int main(void) {
      __int128_t x = -1234567;
    
      printf("%s %s\n", prn_i128(x), prn_i128(x));
    
      return 0;
    }
    

    Gcc kann das mit -std=c99, MSVC beherrscht C99 bislang nicht (und wird es auf absehbare Zeit auch nicht tun). Die allgemeinen Probleme mit Makros bleiben natürlich bestehen.



  • Na, ne. Das mit den PRI-Makros ist vom Tisch. Bei mir heißen sie jetzt: PRIi128, PRId128 und PRIu128. Funktioniert auch zu meiner vollsten Zufriedenheit.

    seldon schrieb:

    Du wirst schon getrennte Speicherbereiche zur Verfügung stellen müssen. In C99 könnte man sich wohl mit compound literals behelfen. Mal eben hingekladdet:

    Was sind compound literals? Hab Google und Co. eben bemüht, jedoch mit eher schlechten Suchergebnissen.

    Kannste mal bitte zu "compound literals" ein kleines Demonstrationsbeispiel machen? Ich kann mir in Bezug auf Deinen Code von eben, nicht viel darunter vorstellen.

    Gibt es auch eine Möglichkeit dem GCC/g++ das ganze mit "-std=c++0x" zu verklickern?

    Ziel sollte folgendes sein.
    Ich möchte die 128-Bit-Integer-Typen wahlweise mit std::cout und/oder printf (C++) ODER nur mit printf() (C) verwenden können.
    Es sollte zudem plattformübergreifend funktionieren (Linux, Unix, Windows). Überall dort, wo die Compiler - vorzugsweise GCC/g++ - eben diese _[u]int128_t-Typen implementiert haben. Ebenso soll bzgl. C++ bis einschließlich C++0x/11-Standard, als auch bis zum aktuellen ANSI-C-Standard unterstützt werden.

    Endziel soll es sein, dass man mit C oder C++ mit diesen Typen umgehen können soll, einschließlich des Outputs durch geeignete Funktionen. Da bei reinem ANSI-C kein std::cout zur Verfügung steht, entfallen damit automatisch Lösungen mit std::cout und Konsorten. Ohnehin soll es beim Einsatz von reinem ANSI-C mit den üblichen Standard-Funktionen, a´la printf() funktionieren. Während std::cout vom Tisch ist und endlich funktioniert, bleibt noch eine funktionierende C-Lösung für printf(). Diese Lösung sollte aber auch bei C++-Anwendung bis zum aktuellen C++-Standard C++0x/11 funktionieren.

    Jemand Ideen, Lösungen, etc.?
    Denn ich weiß nicht mehr weiter und beschäftige mich jetzt schon seit Tagen, wenn nicht sogar Wochen damit.

    Grüße

    Schlitzauge 🙂



  • Ich nochmal. Ich bin gerade am Überlegen, ob man das auch mit dynamischen Char-Array realisieren könnte?



  • 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 🙂


Anmelden zum Antworten