numerical limits aus c++ stdlib nicht IEEE konform?



  • Ich habe hier eine Beschreibung des IEEE Formats für integrale datentypen von docs.sun.com
    Ich denke, daß die Angaben die Sun hier macht auch dem IEEE entsprechen. Dort stehen Bitmuster für float, double, und double precision extended. Diese sind erstmal auf den Prozessor gemünzt. float und double ist eh gleich. Bei extended double werden unterschiede zwischen intel ( 10bytes ) und sparc ( 16 bytes ) erkennbar.

    Erstmal kann ich extended double in visual c++ ( sowohl 6.0 als auch .net2003 ) nicht finden. In den numeric_limits habe ich wohl etwas zu 10byte doubles ( hier long double ) gefunden, intels version des IEEE für double precision extended, aber es scheint nicht zu funktionieren. Wenn ich ein define anlege, um es zu aktivieren, gibt es eine Fehlermeldung "constant too large". Das define heißt _M_M68K und ich habe sie in numeric_limits gefunden. Woanders habe ich sie nicht gefunden, deshalb fühlte ich mich ermutigt, sie einfach selber mal zu definieren, was offensichtlich quatsch war.

    Das nächste Problem sind NaN und infinity.
    numeric_limits kennt nur infinity, das entspricht dem eigentlichen positive infinity.
    Wenn ich eine mathematische operation durchführe wie d = -d * d, wobei d vorher auf den größtmöglichen Wert festgelegt wurde, bekomme ich aber einen Wert der negative infinity entspricht. Was ja soweit wünschenswert ist. Nur sollte auch numeric_limits das wissen.
    Dann ist signaling NaN nicht das was die papers auf den sun Seiten sagen. Und hier in der Tat, div / 0 ergibt das signaling NaN, das numeric_limits kennt. Also einmal stimmt das Ergebnis mit numeric_limits überein mal nicht.
    Jetzt aber zu dem großen Problem: Durch dieses durcheinander ist negative infinity == sNaN. Und zwar bit für bit.
    Es gibt also für mich keine Möglichkeit zu unterscheiden, ob der Wert in meiner Matrix durch 0 geteilt wurde, oder ob er durch fast 0 geteilt wurde.
    Kennt sich da einer aus???



  • Es gibt verschiedene Möglichkeiten, NaNs zu codieren. Es hängt nur von einigen Bits ab, die anderen sind dann egal.

    IEEE kennt nur single und double, alles was darüber ist, haben die jeweiligen CPU-Hersteller erfunden.

    Mit M_M68K wirst wohl nicht weit kommen, schließlich hast du keine veraltete Motorola-CPU in deinem PC.



  • Ringding schrieb:

    Es gibt verschiedene Möglichkeiten, NaNs zu codieren. Es hängt nur von einigen Bits ab, die anderen sind dann egal.

    Das habe ich bemerkt, aber neg inf ist nicht gleich NaN, oder?

    IEEE kennt nur single und double, alles was darüber ist, haben die jeweiligen CPU-Hersteller erfunden.

    Das habe ich den Dokumenten anders entnommen. Leider ist es nicht das Original, sondern nur was Sun dazu schreibt. Dort steht folgendes:

    "IEEE Standard 754 specifies exactly the single and double floating-point formats, and it defines a class of extended formats for each of these two basic formats. The format called double extended in Table 2-1 is one of the class of double extended formats defined by the IEEE standard."

    "Double Extended Format (SPARC Architecture)
    This floating-point environment's quadruple precision format conforms to the IEEE definition of double extended formats"

    "Double Extended Format (x86 Architecture)
    This floating-point environment's double extended format conforms to the IEEE definition of double extended formats. It consists of four fields: a 63-bit fraction, f; a 1-bit explicit leading significand bit, j; a 15-bit biased exponent, e; and a 1-bit sign, s."

    Mit M_M68K wirst wohl nicht weit kommen, schließlich hast du keine veraltete Motorola-CPU in deinem PC.

    Ok, 68K = 68000 ist schon logisch, bin aber nicht drauf gekommen. Muß eine standard library eigentlich alles unterstützen???
    Auch ist mir niht ganz klar, warum eine "veraltete 68000er CPU" ein Format unterstützt, das allen unserer aktuallen CPU`s ( intel und kompatible ) überlegen ist.



  • Ingo Nolden schrieb:

    Auch ist mir niht ganz klar, warum eine "veraltete 68000er CPU" ein Format unterstützt, das allen unserer aktuallen CPU`s ( intel und kompatible ) überlegen ist.

    Weil sie wohl bemerkt haben, dass es sinnlos ist und sie die Fläche (und den Strom) lieber in was anderes stecken sollten.

    Um NaNs zu erkennen, gibt's eine Funktion isnan (oder isnanf für floats), isfinite gibt's dann auch noch. neg inf sollte keine NaN sein, zumindest bei double nicht - möglicherweise bei single, wenn das keine Darstellungsmöglichkeit für -inf hat. Glaub aber nicht.



  • Weil sie wohl bemerkt haben, dass es sinnlos ist und sie die Fläche (und den Strom) lieber in was anderes stecken sollten.

    Tja, und nun haben wir den Salat, beim numerischen lösen von Differentialgleichungssystemen bekommen wir Rundungsfehler, wenn die Schrittweite durch Unstetigkeiten klein wird.

    Um NaNs zu erkennen, gibt's eine Funktion...

    NaN's zu erkennen ist kein Problem wenn sie sich an irgendwelche Regeln halten. Es ist noch nicht mal wichtig ob es dann der Standard ist. Aber wenn sich NaN nicht von -inf unterscheidet, dann habe ich ein Problem.
    Benutzt jemand andere stdlibs als die, die bei MS dabei sind? Wäre toll wenn mir jemand sagen könnte, wie sich andere verhalten.

    mit folgendem Code habe ich experimentiert:

    #include <iostream>
    
    #include <math.h>
    
    #include <limits>
    
    using namespace std;
    
    const __int64 NAN_BITS = 0x7ff0000000000000;
    bool IsNAN( const double d )
    {
    	return ( ( *reinterpret_cast<const __int64*>( &d ) & NAN_BITS ) == NAN_BITS ) 
    		&& ( *reinterpret_cast<const __int64*>( &d ) != NAN_BITS );
    }
    
    void hexout( void* p )
    {
    	cout << "\t";
    	cout.width( 8 );
    	cout.fill( '0' );
    	long* pl = (long*)p;
    	cout << hex << pl[1] << " ";
    	cout.width( 8 );
    	cout.fill( '0' );
    
    	cout << hex << pl[0] << endl;
    }
    
    template< typename T >
    void report( )
    {
    
    	cout << "numeric_limits<double>::\n" << endl;
    
    	double 
    	d = numeric_limits<T>::denorm_min( );	cout << "denorm_min = ";	
    	hexout( &d );
    //	d = numeric_limits<T>::denorm_max( );	cout << "denorm_max = ";	
    //	hexout( &d );
    
    	d = numeric_limits<T>::epsilon( );		cout << "epsilon = ";		
    	hexout( &d );
    
    	d = numeric_limits<T>::infinity( );		cout << "infinity = ";
    	hexout( &d );
    //	cout << "is_bounded = " << numeric_limits<T>::is_bounded( ) << endl;
    //	cout << "is_exact = " << numeric_limits<T>::is_exact( ) << endl;
    //	cout << "is_iec559 = " << numeric_limits<T>::is_iec559( ) << endl;
    
    	d = numeric_limits<T>::max( );			cout << "max = \t";
    	hexout( &d );
    
    	d = numeric_limits<T>::min( );			cout << "min = \t";
    	hexout( &d );
    
    	d = numeric_limits<T>::quiet_NaN( );cout << "quiet_NaN = ";	
    	hexout( &d );
    	d = numeric_limits<T>::signaling_NaN( );cout << "signaling_NaN = ";	
    	hexout( &d );
    
    	// produce an infinite number:
    	d = numeric_limits<T>::max( ); d = d * d;cout << "pos inf = \t";
    	hexout( &d );
    	cout << "Is positive infinity NaN ? " << IsNAN( d ) << endl;
    
    	d = numeric_limits<T>::max( ); d = -d * d;cout << "neg inf = \t";
    	hexout( &d );
    	cout << "Is negative infinity NaN ? " << IsNAN( d ) << endl;
    
    	d = d / 0;
    	cout << "Div by zero returns: ";	hexout( &d );
    
    	cout << "Is div by zero NaN ? " << IsNAN( d ) << endl;
    
    }
    
    int main(int argc, char* argv[])
    {
    
    	report< double >( );
    
    	cout << endl << endl;
    	cout << "sizeof( float ) = " << sizeof( float ) << endl;
    	cout << "sizeof( double ) = " << sizeof( double ) << endl;
    	cout << "sizeof( long double ) = " << sizeof( long double ) << endl;
    
    	getchar( );
    	return 0;
    }
    


  • Wie wär's denn damit: http://www.gnu.org/software/gmp/
    Damit entledigt man sich doch der (meisten) Probleme.



  • Also bei mir passt alles (hab's unter Unix auf x86, MIPS und Alpha probiert).

    quiet_NaN = 7ff80000 00000000
    neg inf = fff00000 00000000

    Und bei neg inf / 0 (also der letzten Rechnung) kommt wieder neg inf raus, das ist vielleicht, was dich wundert. Dein IsNAN ist falsch, die C Funktion isnan macht's richtig. Um bei der letzten Rechnung NAN rauszukriegen, könntest du z.B. neg inf + pos inf rechnen.

    EDIT: Die glibc Implementation (von Sun) schaut so aus:

    int32_t hx,lx;
        EXTRACT_WORDS(hx,lx,x);
        hx &= 0x7fffffff;
        hx |= (u_int32_t)(lx|(-lx))>>31;
        hx = 0x7ff00000 - hx;
        return (int)(((u_int32_t)hx)>>31);
    


  • Okay, ich danke euch allen für eure Hilfe. Insbesondere für das ausführliche testen von RingDing.

    Ist immer gut wenn man am Ende merkt, daß man selbst der Depp ist 😞

    Also mein Fehler war, und das habe ich erst gemerkt als ich Deinen Text zum 256ten mal gelesen habe, ich habe neg inf / 0 gemacht, ohne das beabsichtigt zu haben. Erst dachte ich "was schreibt der da", dann noch mal in den Code geschaut. Also ganz einfach, -x/0 = -inf, +x/0 = +inf
    Was ich eigentlich wollte, einen NaN provozieren, da muß man schon sqrt( -x) schreiben.
    der ergibt dann auch das richtige Ergebnis. Hat mich eh gewundert, denn eigentlich dachte ich, daß das Ergebnis der Operation von der CPU kommt. Zumindest bei division. Bei der Wurzel weiß ich gar nicht wo das implementiert ist.
    Allerdings ergibt swrt( -x ) auch nur einen qNaN und keinen signaling. Aber das ist mir jetzt relativ egal, ebenso wie die numeric_limits. Ich kann mir das zur Not selbst implementieren was ich brauche.

    Ach ja, was für einen header muß ich denn includen für IsNaN aus der CLib. Und gibt es dafür kein äquivalent in den c++ libs?

    Gruß
    Ingo



  • Hey, noch ne interessante Nachricht zu dem Thema.

    Ich habe das ganze noch in ner anderen Group gepostet. Und siehe da einer von Dinkumware ( Hersteller der c++ stdlibs die MS verwendet ) sagt, daß es ein Bug ist, der in späteren releases gefixt wurde. Also was die numeric_limits betrifft. Immerhin. Es kommt Licht ins Dunkel.

    Gruß
    Ingo



  • Ingo Nolden schrieb:

    Ach ja, was für einen header muß ich denn includen für IsNaN aus der CLib. Und gibt es dafür kein äquivalent in den c++ libs?

    Einfach <math.h>. Weiß nicht, ob's in C++ was dafür gibt. Ich bleib immer bei den C Funktionen, weil's die schon Äonen von Jahren gibt und ich nicht einseh, warum ich was anderes nehmen soll, wenn das alte auch super funktioniert.


Anmelden zum Antworten