long double unter GCC



  • Ich teste mein aktuelles Projekt im Moment mit MinGW GCC mit Code::Blocks auf Windows 7. Meine Fragen:
    1. Auf Little-Endian-Maschinen ist das MSB im Byte mit der höchsten Adresse. Nun habe ich hier einen kleinen temporären Hack, der in etwa so aussieht:

    if(std::numeric_limits<T>::is_iec559)
    {
    	unsigned char const* ptr = reinterpret_cast<unsigned char const*>(&x);
    	int mem[sizeof(T)]; //nur zum Debuggen damit ich den Wert sehe
    	std::copy(ptr, ptr + sizeof(T), mem); //dito
    	return (ptr[sizeof(x) - 1] & (1 << (ByteBits - 1))) != 0;
    }
    else
    {
    	...
    }
    

    Auf MSVC und für float und double auch auf GCC funktioniert das. Für den 12 Bit long double auf GCC jedoch nicht. Grund: -0.L hat den Wert 0 0 0 0 0 0 0 0 0 128 0 0. Optimierungen sind ausgeschaltet. Darf der Compiler gemäß C++-Standard die Bytes drehen wie er will? Gibt es eine schnelle und plattformabhängige Implementierung von signbit für Fließkommazahlen?

    2. Kann man bei Code::Blocks keine Buildmodus-Abhängigen Einstellungen machen? Also unter Debug will ich keine Optimierungen aber unter Release selbstverständlich schon. Mühsam...

    3. Wieso kann ich die Werte aus den Watches nicht rauskopieren?

    4. Wieso gibt es keine scopeabhängigen Watches (so wie "Lokal" bei Visual Studio)?



  • std troll schrieb:

    Für den 12 Bit long double auf GCC jedoch nicht.

    Meinst 10 Byte?

    Vielleicht biste auf alignment/padding reingefallen.



  • volkard schrieb:

    Meinst 10 Byte?

    Vielleicht biste auf alignment/padding reingefallen.

    Ja, das könnte sein. Also sizeof(long double) gibt 12 aus. Wie ich dann auf die 10 komme ist mir nicht bewusst. Gibt es also einen plattformunabhängigen Weg das zu implementieren? Wäre das eine Idee?

    if(std::numeric_limits<T>::is_iec559)
    {
        return !!(x & T(-0.L));
    }
    else
    {
        ...
    }
    

    Aber ich weiß nicht, wie das auf verschiedenen Compilern aussieht. -0.L nach float gecastet kann ja auch etwas anderes als nur dieses Signbit ergeben, oder? Und -0.L alleine zwingt den Compiler doch nicht, nur das Signbit als einziges zu setzen...



  • Ich bleibe bei 10 Bytes. Denke nicht, daß Du eine Maschine mit 12 Bytes findest.
    http://en.wikipedia.org/wiki/Long_double



  • volkard schrieb:

    Ich bleibe bei 10 Bytes. Denke nicht, daß Du eine Maschine mit 12 Bytes findest.
    http://en.wikipedia.org/wiki/Long_double

    http://ideone.com/J3cxGk
    Bei mir zu Hause im uebrigen auch.



  • volkard schrieb:

    Ich bleibe bei 10 Bytes. Denke nicht, daß Du eine Maschine mit 12 Bytes findest.
    http://en.wikipedia.org/wiki/Long_double

    Ich glaube dir schon wenn du sagst, dass die long doubles 10 Bytes haben (Hab mich weiter oben vertan, wollte Bytes statt Bits schreiben, die Zahlenwerte bleiben aber dieselben). Die Sache ist nun einmal, dass ich plattformunabhängig sein möchte (wenn möglich). Und theoretisch kann long double auch tatsächlich 12 Bytes groß sein (und nicht nur 10 + Padding wie auf GCC; das ist wenn ich mir das recht überlege auch das einzige was Sinn ergibt, sonst wären zuhinterst nicht diese beiden leeren Bytes...). Laut IEEE Standard gibt es 128 Bit Floats, woher weiß ich denn z.B. dass nicht so einer vorliegt?


  • Mod

    Bei mir gibt's 16 aus.



  • Kellerautomat schrieb:

    volkard schrieb:

    Ich bleibe bei 10 Bytes. Denke nicht, daß Du eine Maschine mit 12 Bytes findest.
    http://en.wikipedia.org/wiki/Long_double

    http://ideone.com/J3cxGk
    Bei mir zu Hause im uebrigen auch.

    Im Link steht doch

    On the x86 architecture, most C compilers implement long double as the 80-bit extended precision type supported by x86 hardware (sometimes stored as 12 or 16 bytes to maintain data structure alignment), as specified in the C99 / C11 standards (IEC 60559 floating-point arithmetic (Annex F)).

    Es geht mir nicht um sizeof, sondern wo der TO sein Vorzeichenbit findet. Da bleibe ich bei 10, auch wenns nach standard 16 sein dürfen.


  • Mod

    Mittels numeric_limits<..>::digits und max_exponent sollte sich ausrechnen lassen, welches Bit für das Vorzeichen verwendet wird und entsprechend in welchem Byte es zu finden ist.



  • GCC 4.8.2 auf x64 ubuntu: long double ist 16, auch mit #pragma pack(1)


  • Mod

    camper schrieb:

    Mittels numeric_limits<..>::digits und max_exponent sollte sich ausrechnen lassen, welches Bit für das Vorzeichen verwendet wird und entsprechend in welchem Byte es zu finden ist.

    Etwa so?

    #include <iostream>
    #include <limits>
    #include <cmath>
    #include <climits>
    
    int main()
    {
    	for (long double x : {-0., 0., -1.1, 1.684})
    	{
    		unsigned char const* ptr = reinterpret_cast<unsigned char const*>(&x);
    		static auto const index = std::numeric_limits<decltype(x)>::digits + (int)std::log2(std::numeric_limits<decltype(x)>::max_exponent) + 1;
                                                                                         // log2 sollte hier durch eine constexpr-Funktion oder __builtin_clz o.ä. ersetzt werden.
    		std::cout << ((ptr[index/CHAR_BIT] & (1 << (index % CHAR_BIT))) != 0);
    	}
    }
    

    Edit: Der obige Code funktioniert gar nicht für double oder float da index==64 bzw. 32 . Man muss also 1 von index abziehen (bzw, das +1 entfernen). Es scheint als ob bei long double irgend ein Padding stattfindet. 😕

    Natürlich ist das nicht zuverlässig. Beispielsweise könnte das Vorzeichenbit ja auch nicht das MSB sein. Man kann aber T(-0)^T(0) als Mask verwenden, oder? Also im Prinzip wie vom TE schon erwähnt.

    if(std::numeric_limits<T>::is_iec559)

    Könnte dir Warnungen geben. Sowas wie "condition of if-Statement is always true" - vielleicht auch nicht wenn der Compiler sieht dass der Ausdruck (Typ-)abhängig ist.

    auch mit #pragma pack(1)

    Betrifft das nicht nur Klassen?


  • Mod

    Arcoth schrieb:

    Edit: Der obige Code funktioniert gar nicht für double oder float da index==64 bzw. 32 . Man muss also 1 von index abziehen (bzw, das +1 entfernen). Es scheint als ob bei long double irgend ein Padding stattfindet. 😕

    float und double speichern das höhchstwertige Bit der Mantisse für normalisierte Zahlen nicht - die 53 signifikanten Stellen von double benötigen also nur 52 bits in der Darstellung. bei den 80bit-long doubles ist das nicht der Fall.



  • Binäre Operatoren gehen auf Fließkommatypen erschreckenderweise nicht. Gibt's einen standardkonformen Workaround?


Log in to reply