// Musterlösung der Aufgabe 1 des Übungsblattes Nr.2, Version b (mit <numbers>)
// Mittels 'g++ -std=c++20 A2_1b.cpp' kompilieren
#include <iostream>              // Ein- und Ausgabebibliothek
#include <cmath>                 // Bibliothek für mathematisches (e-Funktion, Betrag, ...)
#include <limits>                // In dieser Bibliothek ist das Template std::numeric_limits enthalten
#include <numbers>               // Neue Standard-Bibliothek für mathematische Konstanten (seit C++20)
using namespace std;             // Benutze den Namensraum std

int main(){                      // Hauptfunktion
    float e_approx_1;            // Deklaration der float Variable für den approximierten Wert der Zahl e                  
    double e_approx_2;           // Deklaration der double Variable für den approximierten Wert der Zahl e 
    
    unsigned long n = 10000;     // Deklaration der langen ganzen Zahl Variable n und Initialisierung
    e_approx_1 = pow(1+1.0/n,n); // Berechnung des Folgengliedes bei gewähltem n und Übergabe des Wertes an die float Variable
    e_approx_2 = pow(1+1.0/n,n); // Berechnung des Folgengliedes bei gewähltem n und Übergabe des Wertes an die double Variable
    
    // Terminal Ausgabe 
    printf("Die Approximationen der Eulerschen Zahl e, unter Verwendung von n = %li, lauten: \n",n); 
    printf("%24s %24s \n","float-Approximation","double-Approximation");
    printf("%24.20f %24.20f \n",e_approx_1, e_approx_2);                   // Terminal Ausgabe der appr. Werte für e
    printf("%24s %24s \n","Fehler float","Fehler double");
    printf("%24.20Lf %24.20Lf \n",numbers::e_v<long double> - e_approx_1, numbers::e_v<long double> - e_approx_2);   // Terminal Ausgabe der Fehler
    printf("%24.16Le %24.16Le \n\n",numbers::e_v<long double> - e_approx_1, numbers::e_v<long double> - e_approx_2); // Fehler in exponentieller Schreibweise
    
    // Neuer, größerer Wert von n, neue Berechnung, Terminal Ausgabe
    n = 1.0e+8;                  //Entspricht n=10^8
    e_approx_1 = pow(1+1.0/n,n);
    e_approx_2 = pow(1+1.0/n,n);
    
    // Terminal Ausgabe 
    printf("Die Approximationen der Eulerschen Zahl e, unter Verwendung von n = %li, lauten: \n",n); 
    printf("%24s %24s \n","float-Approximation","double-Approximation");
    printf("%24.20f %24.20f \n",e_approx_1, e_approx_2);                   // Terminal Ausgabe der appr. Werte für e
    printf("%24s %24s \n","Fehler float","Fehler double");
    printf("%24.20Lf %24.20Lf \n",numbers::e_v<long double> - e_approx_1, numbers::e_v<long double> - e_approx_2);   // Terminal Ausgabe der Fehler
    printf("%24.16Le %24.16Le \n\n",numbers::e_v<long double> - e_approx_1, numbers::e_v<long double> - e_approx_2); // Fehler in exponentieller Schreibweise
    
    /* Neuer, größerer Wert von n, neue Berechnung, Terminal Ausgabe
    * Das Maschinen-Epsilon eines Gleitkommazahlen-Typs stellt die Differenz zwischen 1 und dem kleinsten darstellbaren Wert größer als 1 dar. 
    * Da innerhalb der Folge (1+1/n)^n bei sehr großem n eine Zahl zu 1 addiert wird, die kleiner als das Maschinen-Epsilon ist, markiert 
    * die ganze Zahl 1/epsilon die Zahl, bei der wohl die beste Approximation vorliegt */
    n = 1.0/numeric_limits<double>::epsilon(); 
    e_approx_1 = pow(1+1.0/n,n);
    e_approx_2 = pow(1+1.0/n,n);
    
    // Terminal Ausgabe 
    printf("Die Approximationen der Eulerschen Zahl e, unter Verwendung von n = %li, lauten: \n",n); 
    printf("%24s %24s \n","float-Approximation","double-Approximation");
    printf("%24.20f %24.20f \n",e_approx_1, e_approx_2);                 // Terminal Ausgabe der appr. Werte für e
    printf("%24s %24s \n","Fehler float","Fehler double");
    printf("%24.20Lf %24.20Lf \n",numbers::e_v<long double> - e_approx_1, numbers::e_v<long double> - e_approx_2); // Terminal Ausgabe der Fehler
    printf("%24.16Le %24.16Le \n",numbers::e_v<long double> - e_approx_1, numbers::e_v<long double> - e_approx_2); // Fehler in exponentieller Schreibweise
}
