Numeric_limits und Typen



  • Servus,

    ich stehe vor ein kleinem Problem und hierzu keine Lösung.
    Ich habe eine Variable x, die eine Fließkommazahl ist, und würde gerne das Epsilon auslesen. Leider geschied das an einer weit entfernten Stelle im Code, was bei der jetzigen Methode zu Fehler führen könnte z.B. bei umschreiben.

    double x;
    ...
    double ep = numeric_limits<double>::epsilon();
    

    Wie kann ich den Typ der Variable x übergeben?

    double ep = numeric_limits<x>::epsilon(); // Führt zu einem Fehler
    

    Gruß,
    Thomas



  • template<typename T>
    T getEpsilonFor(T dummy)
    {
       return numeric_limits<T>::epsilon()
    }
    double ep = getEpsilonFor(x);
    

    manchmal geht auch

    double ep = numeric_limits<typeof(x)>::epsilon();
    

    oder

    double ep = numeric_limits<decltype(x)>::epsilon();
    

    aber das ist noch nicht standard.



  • das erste setzt aktivierte runtime-type-information voraus, wenn ich mich recht erinnere.
    was ich mich allerdings frage, wie man es hinbekommt, das für x mehrere Typen in frage kommen, dass sollte eigentlich gar nicht gehen, da c++ ein stark typisierte Sprache ist.

    im falle von template-funktionen, oder variablen, kann x trotzdem immer nur T (oder wie man das template genannt hat) annehmen., so dass ein numerical_limits<T>:: ausreichen sollte.

    selbst wenn es irgendwie gehen sollte macht das zweite codeschnipsel des thread openers keinen Sinn, da das Ergebnis einen double zugewiesen wird, obwohl x ja scheinbar unbekannten Typs ist (und der des Rückgabewertes zwangsweise vom selben unbekannten Typ ist).

    edit: typos



  • vlad_tepesch schrieb:

    das erste setzt aktivierte runtime-type-information voraus, wenn ich mich recht erinnere.

    Nope.

    selbst wenn es irgendwie gehen sollte macht das zweite codeschnipsel des thread openers keinen Sinn, da das Ergebnis einen double zugewiesen wird, obwohl x ja scheinbar unbekannten Typs ist (und der des Rückgabewertes zwangsweise vom selben unbekannten Typ ist).

    Nein, es kann durchaus mal der Fall eintreten dass man den Typen einer Variable nicht kennt. Dann muss man den Weg ueber eine Funktion gehen die den Typen bestimmen kann oder auf ein typeof() hoffen (manchmal ist BOOST_TYPEOF hier ganz praktisch).

    Idealer ist natuerlich
    typeof(x) y = numeric_limits<typeof(x)>::epsilon() oder aber long double verwenden...



  • vlad_tepesch schrieb:

    das erste setzt aktivierte runtime-type-information voraus, wenn ich mich recht erinnere.
    was ich mich allerdings frage, wie man es hinbekommt, das für x mehrere Typen in frage kommen, dass sollte eigentlich gar nicht gehen, da c++ ein stark typisierte Sprache ist.

    Es geht um das Vermeiden von Fehlern bei coden.

    double x;
    ... numeric_limits<double>::epsilon() ...
    

    Ändere ich jetzt manuell den Type von double auf float, so muss ich auch die zweite Zeile anpassen. Wenn ich das nicht mache ist mein Code fehlerhaft.

    @volkard Danke für deine Post. Ich programmiere nicht beruflich und bin daher nicht auf den neuesten Stand was den C++09 angeht. Endlich mal etwas praktisches 🙂



  • Nein, es kann durchaus mal der Fall eintreten dass man den Typen einer Variable nicht kennt. Dann muss man den Weg ueber eine Funktion gehen die den Typen bestimmen kann oder auf ein typeof() hoffen (manchmal ist BOOST_TYPEOF hier ganz praktisch).

    Konstruier da mal bitte ein beispiel



  • vlad_tepesch schrieb:

    Nein, es kann durchaus mal der Fall eintreten dass man den Typen einer Variable nicht kennt. Dann muss man den Weg ueber eine Funktion gehen die den Typen bestimmen kann oder auf ein typeof() hoffen (manchmal ist BOOST_TYPEOF hier ganz praktisch).

    Konstruier da mal bitte ein beispiel

    Makros.

    Du generierst Code fuer bestimmte mathematische Funktionen und bekommst einen Wert uebergeben - weisst aber nicht welchen Typ er hat.

    Oder aber du hast ein Predicate dass dir einen floating Point Wert liefert, aber du den genauen Typen nicht kennst.

    Natuerlich kann man immer herum tricksen, aber typeof() ist einfach Gold Wert in vielen Faellen. Ein Problem mit fixen Typen ist ja nunmal, dass sie nicht dynamisch auf eine Aenderung eines Typen reagieren.

    Ein

    numeric_limits<typeof(x)>::epsilon()
    

    ist einfach deutlich besser, weil es immer das korrekte epsilon liefert 😉



  • Shade Of Mine schrieb:

    Makros.

    Du generierst Code fuer bestimmte mathematische Funktionen und bekommst einen Wert uebergeben - weisst aber nicht welchen Typ er hat.

    wir sind hier in c++. für sowas benutzt man template funktionen.

    Oder aber du hast ein Predicate dass dir einen floating Point Wert liefert, aber du den genauen Typen nicht kennst.

    wie soll das aussehen? Wie kann mir etwas einen wert liefern, ohne definierten Typ? (mal abgesehen von void zeigern)

    Ein

    numeric_limits<typeof(x)>::epsilon()
    

    ist einfach deutlich besser, weil es immer das korrekte epsilon liefert 😉

    denoch muss auch der rückgabewert vom selben typ sein wie z.

    btw: ich habe typeof zwar schon mal gesehen, kriegs im vcc 8 nicht aber grad nicht compiliert. was braucht man denn. um das benutzen zu können.



  • vlad_tepesch schrieb:

    Shade Of Mine schrieb:

    Makros.

    Du generierst Code fuer bestimmte mathematische Funktionen und bekommst einen Wert uebergeben - weisst aber nicht welchen Typ er hat.

    wir sind hier in c++. für sowas benutzt man template funktionen.

    Mach mir doch mal bitte ganz kurz eine template Funktion fuer max(a,b)

    danke 😉

    templates sind meistens die richtige Wahl, aber leider nicht immer. Deshalb kommt ja auch decltype in den neuen Standard, dann kann man ploetzlich auch Sachen wie max(a,b) als template schreiben ohne einen schreikrampf zu bekommen.

    wie soll das aussehen? Wie kann mir etwas einen wert liefern, ohne definierten Typ? (mal abgesehen von void zeigern)

    template<typename Pred>
    int divide(int num, Pred p) {
      return num/p();
    }
    
    int i() { return 3; }
    double d() { return 0.3; }
    
    int main() {
      cout<<divide(7,i);
      cout<<divide(8, d);
    }
    

    denoch muss auch der rückgabewert vom selben typ sein wie z.

    Oder aber man nimmt einen grossen floating point typ der alles kann... uU nimmt man die variable fuer den man den epsilon wert wissen will selbst...

    btw: ich habe typeof zwar schon mal gesehen, kriegs im vcc 8 nicht aber grad nicht compiliert. was braucht man denn. um das benutzen zu können.

    es ist eine non standard erweiterung. nimm BOOST_TYPEOF



  • template <class T> T get_epsilon(T const&)
    {
        return numeric_limits<T>::epsilon();
    }
    
    // ...
    
    //double ep = numeric_limits<x>::epsilon(); // Führt zu einem Fehler
    double ep = get_epsilon(x);
    

    hm?

    Also als "Notlösung" bis wir decltype haben, was natürlich wesentlich schöner ist, da kein dummer Workaround.



  • hustbaer schrieb:

    template <class T> T get_epsilon(T const&)
    {
        return numeric_limits<T>::epsilon();
    }
    
    // ...
    
    //double ep = numeric_limits<x>::epsilon(); // Führt zu einem Fehler
    double ep = get_epsilon(x);
    

    hm?

    Also als "Notlösung" bis wir decltype haben, was natürlich wesentlich schöner ist, da kein dummer Workaround.

    ja und nein.

    ja: wir warten auf typeof oder sowas. damit wäre das netter.

    nein: im gegensatz zu verdammt vielen workarounds ist das mal ausnahmsweise ein workaround, der recht gut funktioniert und gar nicht notwendig macht, daß man drüber nachdenkt, die sprache zu verändern.

    ja, weil wegen vieler anderer fälle typeof oder was ähnliches auf der wuschliste ganz weit oben steht.



  • Shade Of Mine schrieb:

    Mach mir doch mal bitte ganz kurz eine template Funktion fuer max(a,b)
    danke 😉

    Die brauch man nicht machen, die gibts schon in algorithms:
    std::max und std::min

    template<typename Pred>
    int divide(int num, Pred p) {
      return num/p();
    }
    
    int i() { return 3; }
    double d() { return 0.3; }
    
    int main() {
      cout<<divide(7,i);
      cout<<divide(8, d);
    }
    

    ein typeof von einer funktion geht genauso wenig, oder? da müsste ja sowas wie (double *funname(void)) als typ zurückkommen.
    Ich sehe also nicht, wie das hier weiterhefen sollte.

    Oder aber man nimmt einen grossen floating point typ der alles kann... uU nimmt man die variable fuer den man den epsilon wert wissen will selbst...

    wenn ich aber zb ne bigint klasse habe, die selbst der größte floatingpoint typ nicht mit ausreichender genauigkeit abbilden kann?
    da reicht schon ein int64, da verliert man bei großen zahlen, die letzten stellen bei doubles. wenn man dann mit dem ergebnis von numeric_limits<int64>.max() in einem double weiterrechnet gibts ungenauigkeiten.

    btw: ich habe typeof zwar schon mal gesehen, kriegs im vcc 8 nicht aber grad nicht compiliert. was braucht man denn. um das benutzen zu können.

    es ist eine non standard erweiterung. nimm BOOST_TYPEOF

    bin mir sicher, dass ich schonmal was compiliert habe, wo sowas drin war. __typeof scheint es auch zu gebben, zumindest färbt er es blau.
    Beim compile meckert er trotzdem über unknwon identifier



  • vlad_tepesch schrieb:

    Die brauch man nicht machen, die gibts schon in algorithms:
    std::max und std::min

    He he, du bist darauf reingefallen 😉

    ich kann mit Makros ein

    float a=max(1,0.2);
    

    machen. Mit templates nicht. dh, ich kann es schon machen, aber der Code ist enorm... Vorallem wenn man dann noch ein
    "max(1,0.2)" also ohne Zuweisung supporten will.

    Ist nicht ganz trivial - wenn dir einmal langweilig ist versuche das Problem zu lösen, ist ganz interessant (Alexandrescu hat dazu einen Artikel geschrieben, gibt es sicher auch online).

    ein typeof von einer funktion geht genauso wenig, oder? da müsste ja sowas wie (double *funname(void)) als typ zurückkommen.
    Ich sehe also nicht, wie das hier weiterhefen sollte.

    doch, geht - zumindest mit dem neuen Standard.

    Wichtig ist ja auch dass sowas geht:
    decltype(a<b)

    ein max() mit dem neuen standard wäre dann zB:

    template<typename A, typename B>
    auto max(A const& a, B const& b) -> decltype(a<b) {
      return a<b ? b : a;
    }
    


  • Shade Of Mine schrieb:

    template<typename A, typename B>
    auto max(A const& a, B const& b) -> decltype(a<b) {
      return a<b ? b : a;
    }
    

    return bool?
    jetzt haste mich aber neugierig gemacht, wie max mit dem neuen standard geht.



  • volkard schrieb:

    jetzt haste mich aber neugierig gemacht, wie max mit dem neuen standard geht.

    arg, das war falsch rum - mein fehler.

    muss mal googlen - denn irgendwie gingen expression auswertungen...



  • So, korrekter Code wäre dann:

    template<typename A, typename B>
    auto max(A const& a, B const& b) -> decltype(a<b?b:a) {
      return a<b ? b : a;
    }
    

    manchmal ist es doch so simpel...

    Ob es wirklich geht weiss ich nicht, theoretisch müsste es so gehen und ich habe es in einer c.l.c++.mod diskussion gesehen.

    So, newsgroup thread wieder gefunden:
    http://groups.google.com/group/comp.lang.c++.moderated/msg/15d528fbdf578267

    PS:
    interessanter Punkt hierbei ist, dass max() daduch by value returned - aber ich denke mit type_traits dürfte das eigentlich kein Problem sein:

    template<typename A, typename B>
    auto max(A const& a, B const& b) -> boost::call_traits<decltype(a<b?b:a)>::reference {
      return a<b ? b : a;
    }
    


  • ich kann mit Makros ein

    float a=max(1,0.2);
    

    machen.

    template<typename A, typename B>
    auto max(A const& a, B const& b) -> decltype(a<b?b:a) {
      return a<b ? b : a;
    }
    

    Ich weiß noch nicht so recht, ob ich sowas gut finden sollte.
    schließlich vergleichst du in so einem Fall Äpfel mit Birnen und gibst eins von beiden zurück, dh der tatsächliche typ steht erst zur laufzeit fest.

    Das widerspricht komplett den prinzipien einer streng typisierten Sprache, als die c++ mal konzipiert wurde.
    Wenn man sowas machen will, sollte man, denke ich eine andere Sprache wählen und nicht eine bestehende noch mehr verwässern und aufweichen.



  • vlad_tepesch schrieb:

    Das widerspricht komplett den prinzipien einer streng typisierten Sprache, als die c++ mal konzipiert wurde.
    Wenn man sowas machen will, sollte man, denke ich eine andere Sprache wählen und nicht eine bestehende noch mehr verwässern und aufweichen.

    Bei int/float vielleicht, aber das Problem existiert ja auch bei int32/int64 und sonstigen Vergleichen. Aktuell ist es so, dass ich explizit den größeren Typen angeben muss:

    int64 c=max<int64>(a, b);
    

    aber wenn wir uns das mit dem neuen Standard ansehen:

    auto c = max(a,b);
    

    viel besser.

    Stell dir ein:

    template<typename T>
    T keine_negativen_zahlen_erlaubt(T n) {
      return max(0, n);
    }
    

    Generell ist das Problem dass mich der Typ von variablen nie interessiert. Wenn ich 2 Variablen habe die kompatibel zueinander sind, dann reicht mir das. Bsp: int16, int32 und int64 sind für mich identisch solange ich keine overflows produziere. Deshalb sind templates toll, weil ich Code unabhängig vom Typ schreiben kann.

    Wenn du dir einmal Sprachen mit Type Inference ansiehst wirst du schnell merken wie Bequem es ist den Typen einer variable nicht kennen zu müssen.

    Nur weil du den Typ nicht kennst oder er dich nicht interessiert ist die Sprache ja nicht schwach getypt. mein max() ist streng typisiert und wenn du einen blödsinn machen willst wie zB max("hallo", vector<int>()) dann fliegt dir das um die Ohren. Wenn du aber double und int vergleichen willst, dann ist das ok und er promoted den int zu einem double und liefert dir das. Alles was der Code macht ist das explizite spezifizieren des größeren Typen (wie man es jetzt macht) zu automatisieren.



  • vlad_tepesch schrieb:

    template<typename A, typename B>
    auto max(A const& a, B const& b) -> decltype(a<b?b:a) {
      return a<b ? b : a;
    }
    

    Ich weiß noch nicht so recht, ob ich sowas gut finden sollte.
    schließlich vergleichst du in so einem Fall Äpfel mit Birnen und gibst eins von beiden zurück, dh der tatsächliche typ steht erst zur laufzeit fest.

    C++ bleibt weiterhin statisch typisiert.
    Der Ausdruck a<b?b:a hat in C++ genau einen (statischen) Typ, genau den selben Typ wie er auch ohne decltype hier schon verwendet wird (der "?" Operator ist ja nicht neu). Welcher das ist kannst du dir ja mit dem Funktions-Template Trick anzeigen lassen.
    Und genau diesen Typ liefert dann eben decltype(a<b?b:a) .

    Wenn der Compiler keinen geeigneten Typ finden kann (bzw. genauer: wenn der C++ Standard sagt dass es keinen geeigneten Typ für diesen Ausdruck gibt), dann wird er einen Fehler ausspucken. Auch daran ändert decltype nichts.

    Beispiel: a < b ? "sepp" : 42 wird nicht compilieren, genauso wird decltype(a < b ? "sepp" : 42) nicht compilieren.



  • Das kann doch aber nur als constexpr funktionieren, oder nicht? decltype(a<b?b:a) muss ja zur Compile-Zeit feststehen; wenn a und b Variablen sind, ist das wohl nicht möglich. Kann sein, dass ich da grad quer denke oder was übersehen hab, aber mir scheint das etwas obskur.. 🙂


Anmelden zum Antworten