2012-09-02 4 views
6

Ist es möglich, die Quadratwurzel aus einer ganzen Zahl mit einem metafunction mit der folgenden Signatur zu berechnen:Quadratwurzelmetafunktion?

template<unsigned int N> inline double sqrt(); 

(oder vielleicht mit dem constexpr Stichwort, ich weiß nicht, was das Beste ist). Damit würde sqrt<2>() durch 1.414... zur Compile-Zeit ersetzt.

Was wäre die beste Implementierung für eine solche Funktion sein?

+0

I „Metaprogrammierung sqrt“ gegoogelt und fand dies http://www.informit.com/articles/article.aspx?p=30667&seqNum=3 –

+0

Ich habe schon gesehen, aber es ist nur für den integralen Teil von sqrt einer ganzen Zahl. Ich möchte das Fließkomma-Ergebnis zur Kompilierzeit haben. – Vincent

+0

Da 'sqrt' eine Standardfunktion ist, würde ich stattdessen' sqrtt' verwenden. –

Antwort

8

Das ist nicht sein kann Was Sie suchen, aber ich wollte sicherstellen, dass Sie erkannt haben, dass der Compiler normalerweise mit der Optimierung das Ergebnis zur Kompilierungszeit berechnen wird. wenn Sie diesen Code haben zum Beispiel:

void g() 
{ 
    f(sqrt(42)); 
} 

mit g ++ 4.6.3 mit Optimierung -O2, die resultierende Anordnung Code ist:

9 0000 83EC1C    subl $28, %esp 
    11 0003 DD050000    fldl .LC0 
    12 0009 DD1C24    fstpl (%esp) 
    13 000c E8FCFFFF    call _Z1fd 
    14 0011 83C41C    addl $28, %esp 
    16 0014 C3     ret 
    73     .LC0: 
    74 0000 6412264A    .long 1244009060 
    75 0004 47EC1940    .long 1075440711 

Die sqrt-Funktion ist eigentlich nie genannt wird, und der Wert wird nur als Teil des Programms gespeichert.

daher eine Funktion zu erstellen, die Ihren Anforderungen technisch erfüllt, würden Sie brauchen einfach:

template<unsigned int N> inline double meta_sqrt() { return sqrt(N); } 
+1

Es mag stimmen, dass die meisten Compiler dies zur Kompilierzeit berechnen, aber das ist keine Garantie. Die Funktion sqrt gibt keinen constexpr-Wert zurück. Die Ausgabe für diese Funktion kann nicht als Eingabe für andere Metafunktionen verwendet werden. Dies ist nicht wirklich eine Metafunktion und beantwortet die Frage nicht. – aboveyou00

0

Das Problem, das ich sehe ist, dass MetAP effektiv Mißbräuche Aufzählungen in Variablen. Das Problem ist, dass Enums intern als Integer behandelt werden, was den Versuch ausschließt, einen Gleitkommawert aus ihnen zu erhalten. Sie können jedoch möglicherweise Ihr eigenes Gleitkommaformat erstellen, das zwei Ergebnisse, einen ganzzahligen Teil und einen Exponenten erstellt. Sie müssen dies immer noch in einem Float verarbeiten, wie Out = Sqrt<42>::mantissa * pow(10,Sqrt<42>::exponent);. Eigentlich Bestimmung der Werte für den Leser als Übung, aber Sie werden wahrscheinlich den Eingang skalieren müssen nach oben (durch eine gerade Potenz von 10), die Berechnung der Wurzel, und Speichern der -power/2 Sie früher verwendet.

Um sqrt < 42> zu berechnen, würden Sie zuerst die Exponentenaufzählung auf eine geeignete Potenz wie "-4" setzen (je niedriger, je mehr Dezimalzahlen, aber achten Sie auf Überlauf). Sie multiplizieren dann die Eingabe mit '10^(- 2 * Exponent) '. In diesem Fall erhalten Sie 42 * 10^8 = 4200000000. Sie nehmen dann die Wurzel dieses Wertes und erhalten '64807' als endgültigen Wert. Zur Laufzeit berechnen Sie die "val * 10^Exponent" = "64807 * 10^-4" = 64807 * 0,0001 = 6.4807m und speichern Sie es an einem Schwimmer.

Die zusätzliche Konvertierung funktioniert irgendwie den Zweck, aber Sie können es ein wenig reduzieren, indem Sie den Exponenten als 10^k (dh 10^4) dann tun out=sqrt<x>::mantissa/sqrt<x>::exponent.

bearbeiten Ich habe gerade festgestellt, dass bei der Mantissen/Exponenten-Methode die Wahl des Exponenten beliebig ist, solange er größer ist als der ganzzahlige Anteil der letzten Wurzel. Es kann sogar eine Konstante sein, die das Design Ihrer Metafunktionen erleichtert. Im Fall von 42 zum Beispiel Sie den ‚Exponenten‘ wählen könnte immer sein 6000. Sie dann die Eingabe mit 6000 multiplizieren^2, nehmen Sie die ganze Zahl Wurzel des Produkts, dann zur Laufzeit, das Ergebnis durch 6000 die Wurzel zu bekommen . Anstatt die Ausgabe als * 10^b zu behandeln, wird die Relation sqr (x * b^2) = sqr (x) * b verwendet. Die Mathematik auscheckt:

  • 42 * 6000 * 6000 = 1512000000
  • sqr (1512000000) = 38884
  • 38884/6000 = 6,4806 (quadriert 41,999)
+2

Sie wissen, dass der Computer Potenzen von 10 nicht zum Speichern von Gleitkommawerten verwendet, richtig? Außerdem haben Sie die Konzepte von Exponent und Mantisse vertauscht. –

+0

Fixed :) Ich weiß über die Macht von zehn Sache, aber da ich sowieso mit Dezimalzahlen arbeite, tut es nicht weh. Auch hinzugefügt, dass die Wahl des Exponenten beliebig ist. – Ghost2

+2

Aber wenn Sie Potenzen von zwei verwenden, können Sie 'ldexp' verwenden, um den Exponenten und die Mantisse zu kombinieren, ohne dass eine Multiplikation oder Division erforderlich ist. –

4

Eigen enthält diese meta_sqrt die binäre Suche verwendet:

template<int Y, 
     int InfX = 0, 
     int SupX = ((Y==1) ? 1 : Y/2), 
     bool Done = ((SupX-InfX)<=1 ? true : ((SupX*SupX <= Y) && ((SupX+1)*(SupX+1) > Y))) > 
           // use ?: instead of || just to shut up a stupid gcc 4.3 warning 
class meta_sqrt 
{ 
    enum { 
    MidX = (InfX+SupX)/2, 
    TakeInf = MidX*MidX > Y ? 1 : 0, 
    NewInf = int(TakeInf) ? InfX : int(MidX), 
    NewSup = int(TakeInf) ? int(MidX) : SupX 
}; 
    public: 
    enum { ret = meta_sqrt<Y,NewInf,NewSup>::ret }; 
}; 

template<int Y, int InfX, int SupX> 
class meta_sqrt<Y, InfX, SupX, true> 
{ 
    public: enum { ret = (SupX*SupX <= Y) ? SupX : InfX }; 
}; 
+0

Kann das nicht genug stimulieren. Genau das, was ich brauchte. – romeric