Fragen zu 128-Bit Integer



  • Ethon schrieb:

    So wäre es zb korrekt:

    JA ok, aber dabei ist der Rückgabetyp "string". Ich wollte gerne aber ohne string auskommen, sondern auf Standard-C-String (char[]) setzen, welcher anschließend von den zwei Funktionen jeweils zurückgegeben wird.
    Ich wollte das Konvertieren von stringstream nach c_str() gerne inline in der Funktion ausführen lassen, sodass ich auch wirklich nur C-Strings (char[]) zurückgebe und ich einfach nur die Funktionen in printf() verwenden kann, ohne sonstiges wie ".c_str()".



  • Das kannst du nicht, da du für char* Speicher anfordern müsstest, den du bei der direkten Übergabe an printf() nicht mehr freigeben kannst.

    Und sonst verwendest du eben nicht printf() ...



  • char const* int128_to_cstr(__int128_t x, char* buffer, std::size_t bufflen)
    {
      stringstream sstr;
      sstr<<x;
      buffer[sstr.str().copy(buffer, bufflen - 1, 0)] = 0;
      return buffer;
    }
    

    Die einzig haltwegs sinnvolle Möglichkeit die mir noch einfallen würde.

    char buffer[32];
    printf("%s", int128_to_cstr(123456789, buffer, 32));
    


  • Klick. Stimmt ja. Übergabe als Zeigerparameter.

    Hm. Nagut, macht den Code aber ziemlich hässlich.
    Ihr meint mit OUT-Parameter also sowas:

    char array[32];
    const char* Funktion(char* narray)
    {
      return narray;
    }
    

    ???
    Ich kannte OUT- und IN-Parameter bis dato nicht unter diesen Begriffen.

    Kann man die Bufferlänge nicht einfach via sizeof() inline in der Funktion anwenden?

    Machte es denn einen Unterschied ob / oder:

    const char*
    char const*
    

    ?



  • Schöner könntest du es natürlich mit STL-Containern machen. Aber dein ganzer Ansatz ist so C-ish, dass ich ihn auch so gelassen hab.

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

    bzw

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

    Wären die C++-igeren Methoden, die beide ohne Längen-Parameter auskommen würden ... aber Schwachsinn, der C++-Weg wäre es, nen std::string zurückzugeben.

    Kann man die Bufferlänge nicht einfach via sizeof() inline in der Funktion anwenden?

    Nope, in der Funktion existiert nur noch ein Pointer auf die Daten des Arrays (Syntax-Nazis, bittet diskutiert den Punkt jetzt nicht). Und ein Pointer ist für gewöhnlich 4/8 Byte groß.

    Machte es denn einen Unterschied ob / oder:

    Nöp, finde es nur logischer. Man liest ja von rechts nach links und 'const char*' liest sich als 'Pointer auf ein char, das konstant ist' während 'char const*' sich als 'Pointer auf ein konstantes char' liest.



  • Ethon schrieb:

    Schöner könntest du es natürlich mit STL-Containern machen. Aber dein ganzer Ansatz ist so C-ish, dass ich ihn auch so gelassen hab.

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

    bzw.

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

    Das setzt dann wieder voraus, dass ich ein separates Array übergebe richtig?



  • Ja, irgendwoher muss der Speicher ja kommen.



  • Also seht Ihr keine Möglichkeit, dass ich die __[u]int128_t-Datentypen einfach so direkt in printf() verwenden kann? etwa so:

    __int128_t test = 1024;
    printf("%s",test);
    

    Ist mir jetzt gleichgültig, ob C, C++ oder Hybrid.
    Hauptsache ich kann mit dieser Syntax printf() verwenden.
    Geht das oder nicht?



  • Ich verstehe nicht, warum du unbedingt printf verwenden willst. Und nein, das wird eher nicht gehen.



  • Ich sage ja nicht, dass es "unbedingt" sein muss. Ich meine, mit std::cout hab ich bereits eine funktionierende Lösung.
    Ich möchte aber später die Wahl haben. Insb. wenn ich es mit OpenMP kombiniere, möchte ich eher zu printf() greifen, als zu std::cout.
    Wie schon Anfangs erwähnt, interessiert mich die Möglichkeit mit printf() aus reiner NEUGIER.



  • Schlitzauge schrieb:

    Ich möchte aber später die Wahl haben. Insb. wenn ich es mit OpenMP kombiniere, möchte ich eher zu printf() greifen, als zu std::cout.

    Warum? prtinf hat keinerlei Vorteile, sondern nur Nachteile. Wie du siehst ist ein typsicheres printf mit cout sehr schnell gebaut. Vergiss dabei nicht, dass du mit cout auch eigene Typen ausgeben kannst.



  • Naja, die iostream Bibliothek ist sicherlich Geschmackssache.
    Ich würde aber auch eher was komplett selbst bauen als auf printf setzen.



  • Wenn ich die Wahl hätte, nehme ich bevorzugt C++ und damit auch std::cout.
    Es scheint dennoch eine Grenze des Machbaren zu sein und es interessiert mich brennend das "Unmögliche" irgendwie möglich zu machen.

    Irgendwie gefällt mir die Möglichkeit mit .c_str() dann doch am ehesten.
    Lieber wäre mir eine direkte Nutzung der 128-Bit-Datentypen oder meinetwegen auch mit einfacher Funktions-Syntax (also Funktion mit lediglich der Variablen als Parameter, kein extra temporäres Array oder Bufferlength). Zu dumm, dass printf() nur Zeiger annimt. Einfach Call-by-Value scheint ja nicht möglich zu sein.



  • Was mir nur spanisch vorkommt.
    Wie Ihr auch sehen konntet.
    Rufe ich meine Funktionen in separaten printf() auf, funktionieren sie.
    Nur bei dem Punkt, wo ich bei printf() mehrfach die Funktionen aufrufe, gehts nicht mehr.



  • Meinst du mit deiner kaputten Versdion, die wir kritisiert haben?



  • Ja, meine ich.
    Folgendes:

    cout << "\n+Mit printf(,) UND prn_int128():\n----------------------------------------------------\n";
    printf("x: %s\n",prn_int128(1024));                          //geht
    printf("x: %s, y: %s\n",prn_int128(2048),prn_int128(4096));  //geht nicht
    

    Output:

    +Mit printf(,) UND prn_int128():
    ----------------------------------------------------
    x: 1024
    x: 2048, y: 2048
    

    Ebenso:

    cout << "\n+Mit printf(,) UND [U]INT128ToCSTR(): (EINZELN)\n----------------------------------------------------\n";
    printf("INT128-MIN: %s\n",INT128ToCSTR(INT128_MIN));      //geht
    printf("INT128-MAX:  %s\n",INT128ToCSTR(INT128_MAX));     //geht
    printf("UINT128-MIN: %s\n",UINT128ToCSTR(UINT128_MIN));   //geht
    printf("UINT128-MAX: %s\n",UINT128ToCSTR(UINT128_MAX));   //geht
    
    cout << "\n+Mit printf(,) UND [U]INT128ToCSTR(): (ZUSAMMEN)\n----------------------------------------------------\n";
    //geht nicht
    printf("INT128-MIN:%s\nINT128-MAX:%s\nUINT128-MIN:%s\nUINT128-MAX:%s\n",INT128ToCSTR(INT128_MIN),INT128ToCSTR(INT128_MAX),UINT128ToCSTR(UINT128_MIN),UINT128ToCSTR(UINT128_MAX));
    

    Output:

    +Mit printf(,) UND [U]INT128ToCSTR(): (EINZELN)
    ----------------------------------------------------
    INT128-MIN: -170141183460469231731687303715884105728
    INT128-MAX:  170141183460469231731687303715884105727
    UINT128-MIN: 0
    UINT128-MAX: 340282366920938463463374607431768211455
    
    +Mit printf(,) UND [U]INT128ToCSTR(): (ZUSAMMEN)
    ----------------------------------------------------
    INT128-MIN:-170141183460469231731687303715884105728
    INT128-MAX:-170141183460469231731687303715884105728
    UINT128-MIN:0
    UINT128-MAX:-170141183460469231731687303715884105728
    

    Da sieht man, dass es EINZELN jeweils funktioniert. Die richtigen Werte werden zurückgegeben, printf() übergeben und printf() gibts aus.
    Nur bei Mehrfachanwendung bei printf() nicht.



  • Das ist undefiniertes Verhalten. Vermutlich wird dir der Stack einfach überschrieben, deshalb kommst du an den alten Wert nicht mehr ran.



  • Na, ob das Verhalten den Optimizer überlebt? Es ist reine Glückssache, dass du damit überhaupt einen der beiden formatierten Werte bekommst; vom Standard gedeckt ist das auf keinen Fall. Dass es dieses Verhalten an den Tag legt, wird daran liegen, dass der Stack nach unten wächst und der Buffer in der Funktion länger als 5 + 3 * sizeof(char*) - 16 + was auch immer printf intern auf den Stack legt Byte ist. Häng mal noch ein Dutzend weitere Parameter an printf ran, mit einiger Wahrscheinlichkeit kriegst du dann auch diesen Wert nicht mehr korrekt wiedergegeben.

    Natürlich kann es auch an anderen Dingen scheitern - eine neue libc kann in printf mehr auf den Stack legen lassen, der Optimizer kann sich entscheiden, Parameter in Registern herumzureichen, das Betriebssystem kann dazwischen kommen und alles jenseits des Stackpointers wieder einsammeln, solche Dinge. Man kann an diesem Beispiel sehen, dass "es funktioniert bei mir" nicht auch "das ist so richtig" bedeutet.

    Wenn du den Kram unbedingt in printf stopfen willst, kann man das beispielsweise so machen:

    template<typename T>
    std::string to_string(T const &t) {
      std::ostringstream fmt;
      fmt << t;
      return fmt.str();
    }
    
    printf("%s\n", to_string(dein_int128).c_str());
    

    Und wenn dir die Syntax partout nicht gefällt, kannst du ein Makro drumherumschreiben, etwa

    #define prn_int128(x) to_string(x).c_str()
    

    ...wobei das eigentlich schon wieder ziemlich schlechter Stil ist. Allerdings ist das ganze Vorhaben ziemlich zweifelhaft, so dass es daran nun auch nicht mehr scheitern sollte.



  • Ja, meine ich.

    Genau das Problem, was wir kritisiert haben.
    Deine Funktion holt sich Speicher und schreibt den String hinein. Am Ende der Funktion wird der Speicher freigegeben, du gibst aber einen Zeiger auf den Speicher, der dir nicht mehr gehört, zurück.
    In deinen ersten Beispiel hatte durch Glück keine andere Funktion Interesse an diesem Speicherbereich, deswegen klappt es auch.
    In dem zweiten Beispiel wird die Funktion zuerst korrekt mir 4096 ausgeführt und "4096" wird in den Speicher geschrieben. Dann wird die Funktion mit 2048 aufgerufen, die logischerweise an dem selben Bereich Interesse zeigt, und der Wert wird überschrieben. Jetzt hast du 2 Pointer auf den exakt gleichen Speicherbereich, in dem natürlich der Wert des zweiten Aufrufs steht.

    Deswegen musst du a.) einen std::string nehmen, denn der verwaltet seinen eigenen Speicher und keiner kann ihm reinpfuschen ... oder b.) du übergibst per Parameter einen Buffer, in den die Funktion schreiben soll. Wenn du für jeden Aufruf pro Statement einen eigenen Buffer benutzt dann gibt es auch da keine Kollissionen.

    Jetzt verständlich?

    Na, ob das Verhalten den Optimizer überlebt? Es ist reine Glückssache, dass du damit überhaupt einen der beiden formatierten Werte bekommst; vom Standard gedeckt ist das auf keinen Fall. Dass es dieses Verhalten an den Tag legt, wird daran liegen, dass der Stack nach unten wächst und der Buffer in der Funktion länger als 5 + 3 * sizeof(char*) - 16 + was auch immer printf intern auf den Stack legt Byte ist. Häng mal noch ein Dutzend weitere Parameter an printf ran, mit einiger Wahrscheinlichkeit kriegst du dann auch diesen Wert nicht mehr korrekt wiedergegeben.

    std::stringstream bzw std::string alloziert auf dem Heap, deswegen zeigt sein Zeiger auch auf den Heap, nichts mit Stack. 😉



  • wie waere es mit sowas ?

    class myint
    {
       public:
          myint(int v) : m_value(v) { to_string(); }
    
          operator const char*(void) const { return buffer; }
       private:
          int m_value;
          char buffer[12]; // buffergroesse fuer __int128_t anpassen
          void to_string() { itoa(m_value, buffer, 10); }
    
    };
    
    #define i128(x) (const char*)myint(x) 
    #define PRIi128 "%s"
    
    int main()
    {
        int val = 12;
        printf("test: "PRIi128" -> "PRIi128"\n", i128(12), i128(24));
    }
    

    🙂

    habs der einfachhaltshalber mit nem int gemacht

    Meep Meep


Anmelden zum Antworten