Probleme mit M_PI und dem Visual Studio 2010



  • Erste Frage:
    Ich hab das Problem, dass die Konstante M_PI wohl nicht in der cmath, die vom Visual Studio 2008/2010 mitgeliefert wird, definiert ist. Damit steh ich laut google auch nicht alleine dar. Wie löse ich das am besten?
    Oder muss ich mir tatsächlich

    #define M_PI 3.14159265358979323846
    

    in einer Header selbst als Konstante definieren?

    Zweite Frage:
    Wie kann ich die bestimmte Warnungen ausschalten. Ich versuch das seit einer Stunde zu lösen. Folgendes funktioniert NICHT:

    #pragma warning ( disable : 4244 )
    #pragma warning ( disable : 4018 )
    

  • Mod

    M_PI ist weder C- noch C++-Standard. Es ist jedoch ein reservierter Name, also nenn dein define um oder check vorher wenigstens mittels ifdef, ob es eventuell schon gesetzt wurde. Oder nimm einen const double, du machst ja anscheinend C++, wo das dann eine echte Compilezeitkonstante wäre.

    Oder du guckst einfach mal in die Dokumentation deines Compilers und setzt das Makro _USE_MATH_DEFINES bevor du cmath einbindest.



  • Sepp hat es ja schon beschrieben.

    Hier noch ein Link, falls das nicht auf Anhieb passt:
    http://www.computerbase.de/forum/archive/index.php/t-870206.html

    MfG f.-th.



  • Meine Lösung:

    namespace math 
    { 
      template <typename T> 
      struct constants; 
    
      template <> 
      struct constants<float> 
      { 
        static float  one() { return 1.0f; } 
        static float zero() { return 0.0f; } 
        static float   pi() { return 3.1415926535897932384626434f; } 
        static float    e() { return 2.7182818284590452353602875f; } 
      }; 
    
      template <> 
      struct constants<double> 
      { 
        static double  one() { return 1.0; } 
        static double zero() { return 0.0; } 
        static double   pi() { return 3.1415926535897932384626434; } 
        static double    e() { return 2.7182818284590452353602875; } 
      }; 
    
      template <> 
      struct constants<long double> 
      { 
        static long double  one() { return 1.0; } 
        static long double zero() { return 0.0; } 
        static long double   pi() { return 3.1415926535897932384626434; } 
        static long double    e() { return 2.7182818284590452353602875; } 
      }; 
    } 
    
    ... 
    bla = math::constants<float>:: pi(); 
    ...
    


  • SeppJ schrieb:

    M_PI ist weder C- noch C++-Standard. Es ist jedoch ein reservierter Name

    Echt? Wieso?


  • Mod

    Bashar schrieb:

    SeppJ schrieb:

    M_PI ist weder C- noch C++-Standard. Es ist jedoch ein reservierter Name

    Echt? Wieso?

    Oh, tatsächlich nicht. Irgendwie hatte ich im Kopf, dass M_PI unter die Regeln für reservierte Bezeichner fiele, aber du hast Recht, dem ist nicht so.


  • Mod

    Ich habe bei Google das hier gefunden:
    http://www.c-plusplus.net/forum/206444

    Und M_PI ist sowieso falsch geschrieben, MPI heißt das.



  • @dot: Im Ernst? Du schreibst lieber math::constants<T>::one() als einfach 1.0? Und warum gibst Du solche Zahlengewusel ein, statt Dir pi und e durch die entsprechenden Mathefunktionen berechnen zu lassen? Jeder halbwegs aktuelle Compiler sollte doch 4.0*atan(1.0) bzw. exp(1.0) direkt durch die entsprechenden Werte ersetzen können, oder? Den Sinn von math::constants<T>::e() verstehe ich eh nicht. Wann braucht man die Konstante schon mal explizit?



  • Walli schrieb:

    @dot: Im Ernst? Du schreibst lieber math::constants<T>::one() als einfach 1.0?

    1.0 ist ein double. math::constants<T>::one() dagegen, ist ein T.
    Wenn ich mit doubles rechne, dann schreib ich natürlich 1.0. Aber in generischem Code kann ich das nicht, da ich ja nicht weiß mit welchem Typ ich grad rechne.
    Kannst ja mal ausprobieren was passiert wenn du z.B. auf x64 grad mit floats rechnest und dann eine double Konstante für pi in den Ausdruck reinwirfst. Abgesehen davon dass ich es als furchtbar unsauber empfinde, kann das richtig böse Auswirkungen auf den generierten Code haben, weil der Compiler plötzlich an allen Ecken und Enden unnötige float <-> double Konvertierungen emittieren muss...

    Walli schrieb:

    Und warum gibst Du solche Zahlengewusel ein, statt Dir pi und e durch die entsprechenden Mathefunktionen berechnen zu lassen? Jeder halbwegs aktuelle Compiler sollte doch 4.0*atan(1.0) bzw. exp(1.0) direkt durch die entsprechenden Werte ersetzen können, oder?

    Und wer garantiert mit das? Schreib ich einmal die Zahlenwurst hin, bin ich nichtmehr davon abhängig dass auch wirklich jeder Compiler tatsächlich, immer, überall und unter allen Umständen entsprechendes Constant Folding durchführt...

    Walli schrieb:

    Den Sinn von math::constants<T>::e() verstehe ich eh nicht. Wann braucht man die Konstante schon mal explizit?

    Selten, das stimmt. Aber es schadet auch sicher nicht, sie dabei zu haben...



  • dot schrieb:

    1.0 ist ein double. math::constants<T>::one() dagegen, ist ein T.
    Wenn ich mit doubles rechne, dann schreib ich natürlich 1.0. Aber in generischem Code kann ich das nicht, da ich ja nicht weiß mit welchem Typ ich grad rechne.
    Kannst ja mal ausprobieren was passiert wenn du z.B. auf x64 grad mit floats rechnest und dann eine double Konstante für pi in den Ausdruck reinwirfst. Abgesehen davon dass ich es als furchtbar unsauber empfinde, kann das richtig böse Auswirkungen auf den generierten Code haben, weil der Compiler plötzlich an allen Ecken und Enden unnötige float <-> double Konvertierungen emittieren muss...

    static_cast<T>(1.0) vs. math::constants<T>::one()?

    dot schrieb:

    Und wer garantiert mit das? Schreib ich einmal die Zahlenwurst hin, bin ich nichtmehr davon abhängig dass auch wirklich jeder Compiler tatsächlich, immer, überall und unter allen Umständen entsprechendes Constant Folding durchführt...

    Gut, ich entwickle halt unter der Annahme, dass niemand meinen Code mit uralten Compilern kompilieren will, eben weil ich mir den Luxus leisten kann. Im schlimmsten Fall würde der Code einmal atan unnötig berechnen, was wohl selbst auf einem Taschenrechner zu verschmerzen wäre.



  • Walli schrieb:

    static_cast<T>(1.0) vs. math::constants<T>::one()?

    Ich find die Lösung per Cast unschön. Abgesehen davon ändert der Cast nix dran, dass der Compiler (MSVC) unnötige Konvertierungen emittiert...

    Walli schrieb:

    dot schrieb:

    Und wer garantiert mit das? Schreib ich einmal die Zahlenwurst hin, bin ich nichtmehr davon abhängig dass auch wirklich jeder Compiler tatsächlich, immer, überall und unter allen Umständen entsprechendes Constant Folding durchführt...

    Gut, ich entwickle halt unter der Annahme, dass niemand meinen Code mit uralten Compilern kompilieren will, eben weil ich mir den Luxus leisten kann. Im schlimmsten Fall würde der Code einmal atan unnötig berechnen, was wohl selbst auf einem Taschenrechner zu verschmerzen wäre.

    Nein. Im schlimmsten Fall würde er wohl einmal atan() unnötig berechnen (völlig egal) und dann den unnötig berechneten Wert ständig aus dem Speicher laden (alles andere als egal)...



  • dot schrieb:

    Meine Lösung:
    [...]

    Hmm ... irgendwie so ... kompliziert.

    namespace math_constants {
    
    const long double piL = 3.14159265358979323846L;
    const      double pi  = piL;
    
    }
    

    müsste doch auch schon brauchbar sein.

    Für "generische" Einsen und Nullen schreibe ich auch einfach nur T(1) und T(0).



  • dot schrieb:

    Nein. Im schlimmsten Fall würde er wohl einmal atan() unnötig berechnen (völlig egal) und dann den unnötig berechneten Wert ständig aus dem Speicher laden (alles andere als egal)...

    Ja, wenn man mit uralt-Compilern auf Plattformen arbeitet, wo sich das bemerkbar macht. Aber wozu brauchst Du dann gleich noch generische Programmierung? Irgendwie löst Du scheinbar gerne Probleme, die keine sind.



  • Ich finde dots Lösung elegant.



  • Allow me to demonstrate (Deklaration von math::constants aus Gründen der Übersichtlichkeit weggelassen):

    #include <iostream>
    
    const double pi = 4.0 * atan(1.0);
    
    template <typename T>
    T area(T r)
    {
      return r * r * static_cast<T>(pi);
    }
    
    //template <typename T>
    //T area(T r)
    //{
    //  return r * r * math::constants<T>:: pi();
    //}
    
    int main()
    {
      float r;
      std::cin >> r;
    
      float A = area(r);
    
      std::cout << A;
    }
    

    Aktueller MSVC generiert folgenden Code (x64 Release Build):

    movss       xmm1,dword ptr [r]
     cvtsd2ss    xmm0,mmword ptr [_fmode+4 (13F9935E0h)]
     mulss       xmm1,xmm1
     mulss       xmm1,xmm0
    

    Ohne den static_cast sieht der generierte Code sogar noch wesentlich schlimmer aus (klar, denn der C++ Standard zwingt den Compiler dazu):

    movss       xmm5,dword ptr [r]
     mulss       xmm5,xmm5
     unpcklps    xmm5,xmm5
     cvtps2pd    xmm0,xmm5
     mulsd       xmm0,mmword ptr [_fmode+4 (13F4035E0h)]
     cvtsd2ss    xmm1,xmm0
    

    Für die Variante mit math::constants dagegen, emittiert der Compiler optimalen Code:

    movss       xmm1,dword ptr [r]
     mulss       xmm1,xmm1
     mulss       xmm1,dword ptr [__real@40490fdb (13FBF21E0h)]
    

    Abgesehen davon dass ich die anderen hier präsentierten Lösungen rein auf ästhethischer Ebene unbefriedigend finde, macht es also auf aktuellen Compilern sehr wohl einen realen Unterschied.

    Ja, der Compiler lädt in beiden Fällen den float aus dem Speicher weil der Befehlssatz keine Immediates bietet, es macht also in dem Fall keinen Unterschied wie man pi genau definiert. Das war aber auch nur der denkbare Worst Case...



  • Ich habe hier keinen MSVC, aber ich kann mir irgendwie nicht vorstellen, dass man das cvtsd2ss mit den richtigen Compilerflags nicht wegbekommt. Wenn ich demnaechst mal Zeit habe, probiere ich mal aus, was der gcc daraus macht.



  • Also den MSVC bring ich nichtmal mit dem lockersten Floating Point Model und Optimierung rein auf Geschwindigkeit dazu die Konvertierung zu meiden...


Anmelden zum Antworten