Eulersche Zahl, lustiges Problem (haha)

tr0nix

tr0nix

der-mit-dem-tux-tanzt
Sali zaeme

Ich bin gerade wieder am C lernen und implementiere gerade den Algorithmus zum ausrechnen der eulerschen Zahl.

e^x = x + 1 + x^2/2! + x^3/3! + x^4/4! + ...

Mit dem Exponent 1 und der oben stehenden Formel erhalte ich das Resultat von 2.7182818... was laut wikipedia.org auch korrekt ist.

Ich definiere eine gewisse Genauigkeit die man eingeben kann. Sprich als Beispiel eine Genauigkeit von 1E-10. Wenn x^y/y! nun kleiner ist, als die Genauigkeit, bricht er ab.

Beim y = 16 bricht er bei mir jedoch ab, da das Resultat im negativen Bereich liegt.
resultat: 0.50000000000000000000 - e: 2.50000000000000000000 - schritt: 1
resultat: 0.16666666666666665741 - e: 2.66666666666666651864 - schritt: 2
resultat: 0.04166666666666666435 - e: 2.70833333333333303727 - schritt: 3
resultat: 0.00833333333333333322 - e: 2.71666666666666634100 - schritt: 4
...
resultat: 0.00000000049892481304 - e: 2.71828183008457191505 - schritt: 14
resultat: 0.00000000049895489307 - e: 2.71828183058352701451 - schritt: 15
resultat: -0.00000000346593732254 - e: 2.71828182711758969958 - schritt: 16


Hier mein Code (ja ich weiss es gibt pow() in math.h fuer Exponentialrechnen):
Code:
#include <stdio.h>

int main(void)
{
        double e, resultat, potenz, genauigkeit;
        int x, i, i_fak;

        printf("EULERSCHE ZAHL BERECHNEN\n");
        printf("------------------------\n\n");
        printf("Bitte x eingeben: "); scanf("%d", &x);
        printf("Bitte Genauigkeit eingeben: "); scanf("%lf", &genauigkeit);

        e = x + 1;
        i = 2;
        i_fak = 2;
        potenz = x;

        do
        {
                potenz *= x;
                resultat = potenz / i_fak;
                e += resultat;

                printf("resultat: %1.20lf - e: %1.20lf - schritt: %d\n", resultat, e, i - 1);
                i_fak *= ++i;
        } while( resultat > genauigkeit );

        printf("Die Potenz der Eulersche Zahl mit Exponent %d ist: %1.20lf\n", x, e);
}

Das lustige nun: ich habe herrausgefunden, dass i_fak ueber den gueltigen Bereich des int hinausgeht. So schlau wie ich bin, habe ich daraus also ein "unsigned int" gemacht. Danach kommen jedoch nach dem Ueberschreiten des Grenzwertes so lustige Ausgaben wie:
Code:
resultat: Infinity - e: Infinity - schritt: 33 - potenz: 1.000000 - i_fak: 0

Was ist das? Infinity als Ausgabe fuer einen int? Wie kann man dies am besten abfangen?

Gruess
Joel
 
Die eulersche Zahl laesst sich ueberigens auch wesentlich einfacher ueber lim (1+1/n)^n bestimmen, wobei der limes n gegen unendlich laufen laesst. Waehlst du also n nur gross genug, so bekommst du ein entsprechend genaues Ergebnis. :D

Wenn du eine andere sinnlose Aufgabe brauchst, kannst du ja mal die sqrt von 2 berechnen.

Wird der Wertbereich von unsigned int ueberschritten, gehts wieder bei 0 los, da ((unsigned int)-1 = 0xff...ff) + 1 = 0

mfg
 
Zuletzt bearbeitet:
tr0nix schrieb:
Was ist das? Infinity als Ausgabe fuer einen int? Wie kann man dies am besten abfangen?
Nein, das Inf steht bei den doubles. ;)
Das Problem bei unsigned ist, wie etuli schon gesagt hat, dass es mit 0 weitergeht, sprich, dein resultat wird nicht negativ bzw. so klein wie du es für die Genauigkeit haben willst und du gehst natürlich Richtung unendlich.

tr0nix schrieb:
Das lustige nun: ich habe herrausgefunden, dass i_fak ueber den gueltigen Bereich des int hinausgeht. So schlau wie ich bin, habe ich daraus also ein "unsigned int" gemacht. Danach kommen jedoch nach dem Ueberschreiten des Grenzwertes so lustige Ausgaben wie:
Code:
resultat: Infinity - e: Infinity - schritt: 33 - potenz: 1.000000 - i_fak: 0
Ich habe in deinem Programm die ints durch long ersetzt und die scanfs/printfs angepasst. Funktioniert super, bis ca. 1e-19:
Code:
resultat: 0.50000000000000000000 - e: 2.50000000000000000000 - schritt: 1
resultat: 0.16666666666666665741 - e: 2.66666666666666651864 - schritt: 2
resultat: 0.04166666666666666435 - e: 2.70833333333333303727 - schritt: 3
resultat: 0.00833333333333333322 - e: 2.71666666666666634100 - schritt: 4
resultat: 0.00138888888888888894 - e: 2.71805555555555544700 - schritt: 5
resultat: 0.00019841269841269841 - e: 2.71825396825396836675 - schritt: 6
resultat: 0.00002480158730158730 - e: 2.71827876984127003723 - schritt: 7
resultat: 0.00000275573192239859 - e: 2.71828152557319224769 - schritt: 8
resultat: 0.00000027557319223986 - e: 2.71828180114638451315 - schritt: 9
resultat: 0.00000002505210838544 - e: 2.71828182619849290091 - schritt: 10
resultat: 0.00000000208767569879 - e: 2.71828182828616871092 - schritt: 11
resultat: 0.00000000016059043837 - e: 2.71828182844675936281 - schritt: 12
resultat: 0.00000000001147074560 - e: 2.71828182845823018710 - schritt: 13
resultat: 0.00000000000076471637 - e: 2.71828182845899490871 - schritt: 14
resultat: 0.00000000000004779477 - e: 2.71828182845904287035 - schritt: 15
resultat: 0.00000000000000281146 - e: 2.71828182845904553488 - schritt: 16
resultat: 0.00000000000000015619 - e: 2.71828182845904553488 - schritt: 17
resultat: 0.00000000000000000822 - e: 2.71828182845904553488 - schritt: 18
resultat: 0.00000000000000000041 - e: 2.71828182845904553488 - schritt: 19
resultat: -0.00000000000000000024 - e: 2.71828182845904553488 - schritt: 20
Die Potenz der Eulersche Zahl mit Exponent 1 ist: 2.71828182845904553488
Oder mit mehr Nachkommastellen:
Code:
EULERSCHE ZAHL BERECHNEN
------------------------

Bitte x eingeben: 1
Bitte Genauigkeit eingeben: 1e-19
resultat: 0.500000000000000000000000000000 - e: 2.500000000000000000000000000000 - schritt: 1
resultat: 0.166666666666666666671184175719 - e: 2.666666666666666666738946811499 - schritt: 2
resultat: 0.041666666666666666667796043930 - e: 2.708333333333333333477893622998 - schritt: 3
resultat: 0.008333333333333333333728615375 - e: 2.716666666666666666912419159097 - schritt: 4
resultat: 0.001388888888888888888848890111 - e: 2.718055555555555555854313487529 - schritt: 5
resultat: 0.000198412698412698412698370683 - e: 2.718253968253968254212629696021 - schritt: 6
resultat: 0.000024801587301587301587296335 - e: 2.718278769841269841426104059146 - schritt: 7
resultat: 0.000002755731922398589065186217 - e: 2.718281525573192240101752514825 - schritt: 8
resultat: 0.000000275573192239858906513452 - e: 2.718281801146384479969317360393 - schritt: 9
resultat: 0.000000025052108385441718774685 - e: 2.718281826198492865352684955127 - schritt: 10
resultat: 0.000000002087675698786809897890 - e: 2.718281828286168564116562218480 - schritt: 11
resultat: 0.000000000160590438368216145993 - e: 2.718281828446759002416294181970 - schritt: 12
resultat: 0.000000000011470745597729724714 - e: 2.718281828458229747993643576898 - schritt: 13
resultat: 0.000000000000764716373181981648 - e: 2.718281828458994464408834956792 - schritt: 14
resultat: 0.000000000000047794773323873853 - e: 2.718281828459042259076364200787 - schritt: 15
resultat: 0.000000000000002811457254345521 - e: 2.718281828459045070629437890197 - schritt: 16
resultat: 0.000000000000000156192069685862 - e: 2.718281828459045226754550728110 - schritt: 17
resultat: 0.000000000000000008220635246624 - e: 2.718281828459045234994487239000 - schritt: 18
resultat: 0.000000000000000000411031762331 - e: 2.718281828459045235428168107994 - schritt: 19
resultat: -0.000000000000000000235333429436 - e: 2.718281828459045235211327673497 - schritt: 20
Die Potenz der Eulersche Zahl mit Exponent 1 ist: 2.718281828459045235211327673497
Dann hab ich mir gedacht, jetzt gibts ihm den Rest, also die doubles zu long doubles und die longs zu long longs mit den entsprechenden Anpassungen fuer die Ein-/Ausgabe. Das hat aber leider trotzdem nichts gebracht, keine Ahnung warum.
Hier scheint long und long long überzulaufen. Dass diese Datentypen so wie es aussieht die selben Wertigkeitsbereiche haben, könnte an meiner Architektur liegen, weil ich AMD64 habe. Probier einfach mal ein bisschen rum.

Aber nicht vergessen: unsigned ist nicht ohne Weiteres möglich! Du könntest prüfen, ob i_fak 0 ist, weil eigentlich sollte man unendlich nur bei 1/0 (potenz/i_fak) bekommen. Aber das habe ich nicht getestet.

Viel Spaß dabei noch! ;)
 
Tja, das war mit Ansage. Die Fakultät wächst halt besonders schnell an.

Gruss, Phorus
 
Sali Jungens

Naja, ich mache das Buch zum zweiten mal durch - allerdings liegen etwa 1 1/2 Jahre dazwischen und ich bin nicht am Ball geblieben so, dass ich mich nochmals neu reinarbeiten muss. "long" war mir noch ein Begriff, hier jedoch noch nicht im Buch beschrieben.

Das mit dem "overflow" von normalen, signed Werten kannte ich - also dass das erste bit von 0 zu 1 wurde und somit den gesamten Wert negativierte.

thorus: es waer ja schoen, wenn ich es nach 0 checken koennte, dies ist allerdings nicht moeglich da ich ja nicht in "einer"-Schritten incrementiere sondern exponentiell arbeite.

Ich meine folgendes:
angenommen wir haben eine Grenze von 100. 101 wuerde wieder in 0 Resultieren. Wenn ich jetzt 11 * 9 mache, kriege ich 99. Wenn ich die Grenze ueberschreite mit 12 * 9 kriege ich jedoch nicht 0 sondern 8. Der Check:
(12 * 9) < (11 * 9)
Laesst sich auch nicht bei allen Rechnungen benutzen. Das "ueberschreiben" der Grenze ist IMHO also echt schwer zu detektieren - besonders bei Userinput.


Was ich dazu nicht verstehe ist folgendes:
$ ./euler
EULERSCHE ZAHL BERECHNEN
------------------------

Bitte x eingeben: 1
Bitte Genauigkeit eingeben: 1E-10
resultat: 1.00000000000000000000 - e: 2.00000000000000000000 - schritt: 0 - potenz: 1.000000 - i_fak: 1
....
resultat: 0.00000000208767569879 - e: 2.71828182828616871092 - schritt: 11 - potenz: 1.000000 - i_fak: 479001600
resultat: 0.00000000051758400993 - e: 2.71828182880375290864 - schritt: 12 - potenz: 1.000000 - i_fak: 1932053504
resultat: 0.00000000078189428089 - e: 2.71828182958564701366 - schritt: 13 - potenz: 1.000000 - i_fak: 1278945280
resultat: 0.00000000049892481304 - e: 2.71828183008457191505 - schritt: 14 - potenz: 1.000000 - i_fak: 2004310016
resultat: 0.00000000049895489307 - e: 2.71828183058352701451 - schritt: 15 - potenz: 1.000000 - i_fak: 2004189184
resultat: 0.00000000024959783200 - e: 2.71828183083312469037 - schritt: 16 - potenz: 1.000000 - i_fak: -288522240
resultat: 0.00000000029441775643 - e: 2.71828183112754251383 - schritt: 17 - potenz: 1.000000 - i_fak: -898433024
resultat: 0.00000000912061510012 - e: 2.71828184024815744380 - schritt: 18 - potenz: 1.000000 - i_fak: 109641728
resultat: 0.00000000045603075501 - e: 2.71828184070418821250 - schritt: 19 - potenz: 1.000000 - i_fak: -2102132736
resultat: 0.00000000032259596327 - e: 2.71828184102678438450 - schritt: 20 - potenz: 1.000000 - i_fak: -1195114496
resultat: 0.00000000026509362513 - e: 2.71828184129187810925 - schritt: 21 - potenz: 1.000000 - i_fak: -522715136
resultat: 0.00000000115948245156 - e: 2.71828184245136039721 - schritt: 22 - potenz: 1.000000 - i_fak: 862453760
resultat: 0.00000000028416993933 - e: 2.71828184273553041805 - schritt: 23 - potenz: 1.000000 - i_fak: -775946240
resultat: 0.00000000048165369515 - e: 2.71828184321718424599 - schritt: 24 - potenz: 1.000000 - i_fak: 2076180480
resultat: 0.00000000040965391598 - e: 2.71828184362683833442 - schritt: 25 - potenz: 1.000000 - i_fak: -1853882368
resultat: 0.00000000067349881102 - e: 2.71828184430033692465 - schritt: 26 - potenz: 1.000000 - i_fak: 1484783616
resultat: 0.00000000034255542974 - e: 2.71828184464289224209 - schritt: 27 - potenz: 1.000000 - i_fak: -1375731712
resultat: 0.00000000032749804822 - e: 2.71828184497039027079 - schritt: 28 - potenz: 1.000000 - i_fak: -1241513984
resultat: 0.00000000070957910447 - e: 2.71828184567996933296 - schritt: 29 - potenz: 1.000000 - i_fak: 1409286144
resultat: 0.00000000135465101762 - e: 2.71828184703462039096 - schritt: 30 - potenz: 1.000000 - i_fak: 738197504
resultat: 0.00000000046566128731 - e: 2.71828184750028167826 - schritt: 31 - potenz: 1.000000 - i_fak: -2147483648
resultat: 0.00000000046566128731 - e: 2.71828184796594296557 - schritt: 32 - potenz: 1.000000 - i_fak: -2147483648
resultat: Infinity - e: Infinity - schritt: 33 - potenz: 1.000000 - i_fak: 0
resultat: Infinity - e: Infinity - schritt: 34 - potenz: 1.000000 - i_fak: 0
Teilweise sind die i_fak im negativen Bereich. Im Code steht jedoch:
unsigned int i_fak;
i_fak *= ++i;

Wobei i immer positiv ist und nur inkrementiert wird. Wie wird das negativ?
 
Das Problem ueber die Fakultaet zu loesen ist sinnlos, wie gesagt. Die Faklutaet waechst zu stark. Besser ist eine Loesung, die sich durch ausklammern einer beliebigen Partialsummer der e-Reihe ergibt. (siehe Anhang)

Code:
#include <stdio.h>

double e_help(double x, unsigned int i, unsigned int n)
{
	 if(i == n)
		  return(1);
	 return( 1 + (x / i) * e_help(x, i + 1, n));
}

double e(double x, unsigned long n)
{
	 return( e_help(x, 1, n));
}

int main()
{
	 printf( "%.40lf\n", e(1.0,100000));
}

[edit]
Wobei i immer positiv ist und nur inkrementiert wird. Wie wird das negativ?
Das liegt in einer falschen Darstellung deinerseits. Etwa mit printf und %d. Du muesstest %u verwenden, oder entsprechend anderes.
 

Anhänge

  • foo.png
    foo.png
    5,8 KB · Aufrufe: 20
Zuletzt bearbeitet:
$ ./test
Segmentation Fault(coredump)

Hab ich was verpasst? :-) Habs auf Solaris 10 kompiliert.

// Edit
Aaah, mit 1000 Durchgaengen funktionierts! Interessanter Code, zwar wohl nicht das, was in der Beispielaufgabe erwartet war, aber interessant!
 
Zuletzt bearbeitet:
Du hast n zu gross gewaehlt vermutlich. Oder n ist allg. bei dir zu gross. Loesung ist die Ueberfuehrung in eine Schleife. :D

Code:
double e(double x, unsigned int n)
{
	 unsigned int i;
	 double r = 1;

	 for( ; n > 0 ; n --)
		  r = r * (x / n) + 1;
	 return( r);
}

Womit wird dann wieder waeren bei (1 + x/n)^n = e^x. ;o)

mfg
 
Zuletzt bearbeitet:
Jeps, habs oben noch mit nem Edit nachgefuegt. Sag mal, bist du eigentlich Mathestudent oder was :)! Hab einfach die gegebene Formel versucht umzusetzen, diese umzuformen war ich nicht imstande!
 
Na du hast Glueck.. ich zweifle hier unterdessen langsam an mir selbst! Jetzt ist die naechste Aufgabe, ein Programm zu schreiben, dass den Sinus berechnet.

Die zu implementierende Formel ist:
sin(x) = x - x^3/3! + x^5/5! - x^7/7! + x^9/9! - ...
(Siehe Reihenentwicklung Sinus auf http://de.wikipedia.org/wiki/Sinus)

Wieso stimmt das, was ich unten habe nicht? Ich kriege total verrueckte Werte mit der Zeit!
$ ./main
x: 3
Rechne Summe 3.000000 + (-1 * 4.500000) = -1.500000
Rechne Summe -1.500000 + (1 * 2.025000) = 0.525000
Rechne Summe 0.525000 + (-1 * 0.433929) = 0.091071
Rechne Summe 0.091071 + (1 * 0.054241) = 0.145312
Rechne Summe 0.145312 + (-1 * 0.004438) = 0.140875
Rechne Summe 0.140875 + (1 * 0.000256) = 0.141131
Resultat: 0.141131

Sollte sein: 0.05233595624294383

Code:
#include <stdio.h>

int main(void)
{
        int i, i_switch;
        double x, x_exp, res, summe, fak;

        printf("x: "); scanf("%lf", &x);

        i = 3;
        x_exp = x;
        fak = 1;
        res = 0;
        i_switch = -1;
        summe = x;

        do {
                x_exp *= x * x;
                fak *= (i - 1) * i;
                res = x_exp / fak;
                i += 2;

                printf("Rechne Summe %lf + (%d * %lf) = ", summe, i_switch, res);

                summe += (i_switch * res);

                printf("%lf\n", summe);

                i_switch *= -1;
        } while ( i != 15 );

        printf("Resultat: %lf\n", summe);
}

Ich habe es sogar haendisch nachgerechnet und bis zu der Fakultaet 9 dieselben Resultate gekriegt.

Mach ich hier in Mathe was falsch? Ich weiss der while-Abbruch ist ein wenig unsauber, aber bin am debuggen und viel scheint sich an dem Resultat danach auch nicht zu aendern.
 
Die Formel von oben ist soweit richtig. Allerdings reicht es, wenn du berechnest fuer jeden Summand der Summe, und wenn i dein Zaehler von 0 bis k ist, mit 1er Schritten:

Code:
(-1)^k * x^(2k+1)
-------------------
(2k+1)!
 
etuli schrieb:
Die Formel von oben ist soweit richtig. Allerdings reicht es, wenn du berechnest fuer jeden Summand der Summe, und wenn i dein Zaehler von 0 bis k ist, mit 1er Schritten:

Code:
(-1)^k * x^(2k+1)
-------------------
(2k+1)!
Hm, hey langsam macht mir Mathe wieder spass! Ich hatte irgendwie immer Probleme, aber wenn ich solche mathematischen Aufgaben zusammen mit Programmieren lösen soll, finde ich wieder gefallen an all den Formeln und besonders Motivation, mich darin zu vertiefen!

Du hast die letzte Aufgabe bereits mit Einzelschritten und einem rekursiven Loop gelöst - aber ändert sich etwas am Resultat? Was mich verwirrte, war ja, dass sin(3) im Taschenrechner nicht zu demselben Resultat kam, wie ich mit meinem Code.

Werde mal versuchen, deinen Code ähnlich deiner ersten Lösung (mit rekursiven Aufrufen) zu lösen!

Thx auf alle Fälle für deine Geduld :winke:
 
tr0nix schrieb:
Sali Jungens
Naja, ich mache das Buch zum zweiten mal durch - allerdings liegen etwa 1 1/2 Jahre dazwischen und ich bin nicht am Ball geblieben so, dass ich mich nochmals neu reinarbeiten muss. "long" war mir noch ein Begriff, hier jedoch noch nicht im Buch beschrieben.

Öhm, in einem ordentlich Buch über's Programmieren sollten (finde ich) am Anfang wenigstens Datentypen beschrieben werden. Was hast Du denn für ein Buch, wenn ich mal nachfragen darf?!

Mfg, Lord Kefir
 
Es ist schon ein schlaues Buch. Es hat einfach zuerst die "einfachen" Datentypen beschrieben wie int und char und kommt dann zu den Schleifen und Abfragen. Also basic Syntax und geht dann weiter mit den erweiterten Datentypen.
 
Ach so. Sicherlich Geschmacksache, was man besser findet. Ich dachte nur, dass 'long' gar nicht erwähnt worden wäre - was mir dann doch ein wenig komisch vorkam ;)

Mfg, Lord Kefir
 

Ähnliche Themen

C Code Hilfe!!! gesucht bei Dezimalzahl in Binärzahl for loop

Keine grafische Oberfläche (Debian Installation)

ip6tables Problem

Ausführbare C-Datei von Mac OS auf Embedded Linux ausführen

Prozesskommunikation mit PIPES - wie funktioniert das?

Zurück
Oben