C Schleifenproblem

antred schrieb:
Versteh mich nicht falsch, ich habe gar nichts gegen const. Wo es Sinn macht (Referenz/Pointer-Argumente), sollte man es IMMER einsetzen, aber für einen Übergabeparameter wie long long firstNumber sehe ich diesen Sinn nur begrenzt. Überleg dir mal, was dieses const eigentlich aussagt:



Das ist dem Aufrufer aber vollkommen Wurscht, da die Funktion sowieso nur eine Kopie des übergebenen Werts in die Finger bekommt. Den einzigen Nutzen, den das const hier hat, ist eben zu erzwingen, dass du in deinem Funktionsrumpf den Wert des Parameters nicht verändern kannst ... was man ja auch, wie ich schon gesagt habe, nicht tun sollte. Aber nur deswegen die Schnittstelle der Funktion mit einem für den Aufrufer absolut nutzlosen const künstlich aufzublähen, halte ich nicht für guten Stil.

du hast damit durch aus recht. nur der sinn von const bei funktionsparametern (die kopien darstellen) ist ein ganz anderer:
es ist sinnvoll, wenn mehr als ein entwickler an dem code arbeitet. denn dann, kann man sich einfach darauf verlassen, dass man über die parametervariable immer den der funktion original übergebenen wert bekommt und nicht einen zwischenzeitlich veränderten.

das kann nämlich sehr leicht übersehen werden, wenn die variable innerhalb der funktionen abgeändert wurde.

das habe ich auch erst in meiner firma hier gelernt, da sich diese problematik für mich bisher nie gestellt hat. natürlich passiert so etwas auch ohne const (oder in java final) nicht all zu oft, aber es hilft. die erfahrung wurde hier schon zu oft gemacht und ich kann dies bestätigen.
 
@Dese
genau das hatte ich dazu ja auch schon geschrieben ;)
 
Dese schrieb:
du hast damit durch aus recht. nur der sinn von const bei funktionsparametern (die kopien darstellen) ist ein ganz anderer:

Und den habe ich in meinem Beitrag ja auch explizit angesprochen. Nur betrachte ich das persönlich nicht als Rechtfertigung, die Funktionssignatur zu vergößern. Aber gut, jeder wie er meint. ;)
Ergänzung ()

Ich habe bezüglich des const-value-parameter Themas mal ein wenig Google bemüht und bin auf etwas (für mich) Neues gestoßen, das meine Einwände gegen solche Parameterdeklarationen aufweicht. Und zwar sagt der Standard in Kapitel 8.3.5 in Absatz 3:

After producing the list of parameter
types, several transformations take place upon these types to determine the function type. Any cv-qualifier
modifying a parameter type is deleted. [Example: the type void(*)(const int) becomes
void(*)(int)—end example] Suchcv-qualifiers affect only the definition of the parameter within the
body of the function; they do not affect the function type. If astorage-class-specifiermodifies a parameter
type, the specifier is deleted. [Example:register char*becomeschar* —end example] Such
storage-class-specifiers affect only the definition of the parameter within the body of the function; they do
not affect the function type. The resulting list of transformed parameter types is the function’sparameter
type list.


Heißt also, für den Compiler ist

Code:
void getPrimsInRange(const long long firstNumber, const long long lastNumber, long long* listOfPrims, const long long sizeOfList);

haargenau die selbe Funktion wie

Code:
void getPrimsInRange(long long firstNumber, long long lastNumber, long long* listOfPrims, long long sizeOfList);

Man kann also in der (für den Aufrufer relevanten) Deklaration im Header die Funktion ohne das const spezifieren und in der (für den Aufrufer absolut irrelevanten) Definition das const reinnehmen. Dann hätte man das beste beider Welten, nach außen eine Funktionssignatur ohne Ballast und nach innen eine Definition, die zusätzlichen Schutz vor unbeabsichtigten Veränderungen an Parameterwerten gibt. Man lernt nie aus. :)
 
entschuldigt beide, dass ich übersehen hatte, dass dieser punkt bereits angesprochen wurde.

@antred: ich kann dich verstehen. exakt so dachte ich bis vor rel. kurzem auch. das hat sich aber seit dem ich an einem großprojekt mit vielen entwicklern arbeite geändert.

bin selbst schon oft genug in die falle gestolpert eine funktion anpassen zu wollen bzw. zu erweitern und übersah, dass der im übergebene parameter nicht mehr den übergebenen wert hatte. ein einfaches aber sehr häufiges beispiel sind switches oder mehrere if-anweisungen, die fallunterscheidungen ausführen.

du willst eine neue hinzufügen (z.b. für ein neues eingabefeld, dass initialisiewrt werden soll) und brauchst ne info aus den parametern. irgend ne pflaume hat den wert aber überschreiben, weil er damals das letzte statement geschrieben hat ("nach mir kommt ja nix mehr").

Ohne sich auf so etwas verlassen zu können ist man gezwungen den ganz anderen Kram nachzuvolziehen, obwohl dies oft völlig überflüssig ist. das stellt in solchen fällen ne enorme erlecihterung dar. da dies hier schon oft zum problem wurde ist das bei uns mittlerweile eine pflicht.
 
Bei der Variante, die ich vorgeschlagen habe hast du die Zahl 19 im Primzahltest vergessen. Das heißt alle Zahlen, die durch 19 teilbar sind werden fälschlicherweise als Primzahl ausgegeben.

Wenn du im Funktionsaufruf sicher stellst, dass nur ungerade Zahlen geprüft werden, dann kannst du die erste If-Anweisung sogar noch einsparen und direkt mit x%3 anfangen zu prüfen. Da diese Abfrage ja tatsächlich bei jeder Zahl vorgenommen wird könnte man hier schon drüber nachdenken :)

EDIT: Achja in der Schleife in der Funktion würde ich auch nicht bei 23 anfangen zu prüfen, sondern erst bei der nächsten Primzahl, welche hier 29 wäre.
 
Zuletzt bearbeitet:
Okay Danke. Übrigens muss dann noch etwas geändert werden, denn bei den ersten Primzahlen bis 23 würde der Test dann negativ ergeben.
@Fonce Aber wie soll ich den die Variable als Parameter übergeben, wenn außerhalb der Funktion die Anzahl der schon mit Primzahlen belegten Felder gar nicht bekannt ist?
 
So, bin nun bei Problem 13. Das Programm ist noch nicht fertig, allerdings muss davor noch etwas geklärt werden:
Code:
#include "stdafx.h"
int gibPotenz(int basis, int exponent)
{
	int potenz;
	potenz = basis;
	if(exponent == 0)
		return 1;
	while(exponent > 1)
	{
		potenz = potenz * basis;
		exponent--;
	}
	return potenz;  
}

int main(void)
{
	int test;
	long double zahlen[50];
	char folge []= "";	//String der Übersichtlichkeit zulieben weggelassen
	for(int i = 0; i<100; ++i)
	{
		zahlen[i] = 0;
		for(int x=0; x<=49; ++x)
			zahlen[i]+=folge[i*50+x]*gibPotenz(10, 49-x);
	}
	printf("%Lf", zahlen[0]);
	scanf("%i", &test);
	return 0;
}

Hier teste ich praktisch nur, ob die Zahlen richtig abgespeichert wurden, in diesem Fall zahlen[0]. Allerdings kommt da nicht das gewünschte Ergebnis, sondern 1029135530.000000. Liegt das an der Darstellung mittels %Lf? Ansonsten enthält wohl die Schleife einen Fehler.

BTW:
Gibt es eine Möglichkeit, den String nach dem Einfügen gleich in eine Zeile zu bekommen, ohne jede Zeile einzeln hochzuziehen?
 
Okay, Danke.

Was den Code angeht, habe ich das jetzt einfach mal getestet und zahlen[0] einen 50-stelligen Wert gegeben. Doch dann kommt der Fehler: Konstante zu groß. Doch eigentlich sollte der Datentyp mit solch großen Werten zurechtkommen:
http://openbook.galileocomputing.de/c_von_a_bis_z/005_c_basisdatentypen_008.htm
Nur die Genauigkeit ist dann nicht mehr gegeben, evtl. hat es etwas damit zu tun. Was kann ich denn jetzt tun?
 
vermutlich hast du vergessen, das literal explizit als fließkommazahl zu markieren:

diese version kompiliert bei mir nicht:
Code:
    double t = 12345678901234567890123456789012345678901234567890;

diese schon:
Code:
    double t = 12345678901234567890123456789012345678901234567890.0;

grund ist, dass zahlen erstmal als int interpretiert sind, und 50 stellen sind zu viel für einen int. ob danach nach double gecastet wird, interessiert dann schon nicht mehr.
durch das ".0" wird die zahl zu einem double-literal.
du möchtest ein long double, daher wäre für dich
Code:
long double t = 12345678901234567890123456789012345678901234567890.0L;
die korrekte version.
 
Danke.

Hab gerade ein Problem mit der Funktion gibPotenz. Seit ich die Datentypen ausgetauscht habe, liefert sie falsche Ergebnisse:
long double gibPotenz(long double basis, int exponent)
{
long double potenz;
potenz = basis;
if(exponent == 0)
return 1;
while(exponent > 1)
{
potenz = potenz * basis;
exponent--;
}
return potenz;
}

Umwandlungstechnisch gäbe es ja nichts zu tun. Warum geht es dann nicht?
 
so eine simple funktion solltest du auch selbst per printf debuggen können: schau doch einfach mal, was passiert, wann haben die variablen welche werte, welche sollten sie haben?
 
Die Funktion hat auch auch funktioniert, bevor ich die Datentypen ausgetauscht habe. Also muss das Problem da liegen. Wenn ich über Printf debugge, kommt auch, wenn ich den Exponenten auf long double setze, solche Werte:
4616189618054758400 und 4616189618054758400
4620693217682128896 und 4613937818241073152
4625196817309499392 und 4611686018427387904
4629700416936869888 und 4607182418800017408

Links ist die Variable potenz, rechts exponent. Warum das nicht geht, ist mir schleierhaft.
EDIT: Hat sich erledigt, falsches Formatierungszeichen verwendet (lld).

@Hancock Von der Pow-Funktion war mir gar nichts bekannt. Danke!
 
Zuletzt bearbeitet:
welchen compiler verwendest du? gcc bzw g++ spuckt mir warnungen bei falschen formatierungszeichen aus.
 
Hab länger nicht mehr in den Thread geschaut, deswegen so spät. Ich arbeite mit Visual C++.
Stehe aber gerade vor einem anderen Problem (Nummer 16).

Code:
#include "stdafx.h"
#include <math.h>
double gibRest(double teiler1, double teiler2)
{
	while(teiler1 >= teiler2)
		teiler1-=teiler2;
	return teiler1;
}
   
int main()
{
	int test;
	double ziffern[350];
	long double zweihochtausend = pow((double)2, (double)1000);
	double summe = 0;
	for(int i = 1; i<= log10(zweihochtausend)+1; i++)
	{
		ziffern[i] = gibRest(zweihochtausend, pow((double) 10, (double) i)) / pow((double) 10, (double)i-1);
		summe += ziffern[i];
	 
	}
	printf("%lf", ziffern[311]);
	scanf("%i", &test);
	return 0;
}

Da der Modulo-Operator keine Double-Werte akzeptiert und eine Umwandlung auch nicht funktionieren würde, habe ich mir eine eigene Funktion geschrieben. Im Moment ist die noch extrem ineffizient. Wenn ich es effizienter machen will, z.B. so, stehe ich allerdings vor einem Problem:

Code:
double gibRest(double teiler1, double teiler2)
{
	double multiplikator = teiler1 / teiler2;
        return teiler1 - teiler2 * (int) multiplikator;
}

Hier mal ein Beispiel, das das Vorgehen verdeutlicht:
Wir haben 10 als teiler1 und 3 als teiler2;
Zunächst wird 10 durch 3 geteilt, was den Multiplikator ergibt, also 3,33...
Nun wird 10 minus 3 mal 3 (Nachkommastellen fallen weg) zurückgegeben.

Das Problem daran ist nun, dass der Multiplikator in diesem Programm zu groß für int und auch long long sein wird.

Und hier habe ich sowieso das Problem, dass nur die ersten paar Stellen der Zahl zweihochtausend gespeichtert werden. Muss ich hier nun mit einem völlig anderen Ansatz rangehen oder kann ich dieses Problem umgehen?
 
Was hantierst du da mit doubles, die sind doch viel zu ungenau...

Du musst die Zahl manuell abspeichern (so weit ich das sehe, gelöst hab ich das Problem noch nicht).

Tipp: Denk mal an die 4.? Klasse zurück, wie man da Zahlen gerechnet hat und setzt das um (Am besten nicht mit der Menge der arabischen Zahlen sondern mit der Menge der int's als Ziffern).
 
Zuletzt bearbeitet:
Dass double zu ungenau ist, ist mir klar. Und genau das ist das Problem. Aber welche Datentyp soll ich denn verwenden? long double ist ja auch nicht viel genauer.
Leider weiß ich auch nicht, was du mit manuell abspeichern meinst. Ziffer für Ziffer? Ich muss ja irgendwie erst die Zahl ausrechnen, aber dann sind nur die ersten paar Ziffern gespeichert.

Ein Einfall ist mir bei Lesen deines Tipps gekommen (auch wenn ich mir dabei etwas blöd vorkam). Meinst du eine Art schriftliche Multiplikation von 2 oder irgendeiner Potenz von zwei auf dieses Problem übertragen? Die Zahl dann in mehrere aufteilen bei jedem Multiplikationsschritt. Am Ende die einzelnen Ziffern mithilfe der Zahlen zusammensetzen. Allerdings wäre das extrem komplizierter Ansatz.
 
Dir sollte klar sein, dass es bei projekt euler nicht darum geht Programmieren zu lernen sondern sich mit Mathematischen Problemen zu beschäftigen. Es wird also leider immer öfter vorkommen, dass einfach viel Programmier-Können vorausgesetzt wird und man sich auf die Mathematik fokussiert.

Bei deiner Aufgabe 16 wirst du wahrscheinlich einfach die Grenze jeden normalen Datentyps von C übersteigen.
Der Tipp den Hancock dir also geben wollte lautet etwas deutlicher: Du brauchst irgend eine Art von anderem Datentyp der mit "unendlich" großen Zahlen umgehen kann. (Begrenzt nur durch den gesamten Speicher, den das Programm bekommt und nicht etwa durch die paar Datentypen die C bietet)
Es gibt fertige BigInteger Bibliotheken für C. Google einfach mal nach "biginteger C" ohne ""

Der Sinn der Aufgabe ist vermutlich einfach nur sich mit dem Thema "exakte ganzzahlen größer als long long int" zu beschäftigen. Früher oder später wirst du auch bei anderen Aufgaben mit Zahlen hantieren die über jeder normalen Datentyp-Grenze liegen.

Alternativ wechselt du die Sprache zu Lisp (halle ASDFMAN! :D), denn diese kann ohne Aufwand mit beliebig großen Zahlen rechnen:
http://www.compileonline.com/execute_lisp_online.php
Code:
(write (expt 2 1000))
=>
10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046474983581941267398767559165543946077062914571196477686542167660429831652624386837205668069376

Noch eine Alternativ wäre ein eigener Algorithmus für das Berechnen von a^b, welcher das Ergebnis "Blockweise" ermittelt. Also der immer nur so viele Stellen berechnet, wie gerade in einen int passen und sich irgendwie merkt, wo er weiter rechnen muss nachdem er den int abgeliefert hat um die nächsten Stellen zu generieren.

Sowas ähnliches wollte ich auch immer schonmal schreiben als du so viel Zeit mit dem Primzahlfinden verbracht hast: Es ist durchaus legitim sich einfach die ersten paar Millionen Primzahlen als fertige Datei aus dem Netz zu holen. Die Probleme werden für sich schon kompliziert genug ;)
 
Zuletzt bearbeitet:
Die Aufgaben sind natürlich primär mathematische Probleme, wobei ich durch die Aufgaben trotzdem mein Programmiererfahrung verbessern kann.

Wo du das mit der Programmiersprache gerade erwähnst. Ich habe mich eh schon mal gefragt, vielleicht eine andere Programmiersprache zu lernen.
Zu Vorinformation:
Ich bin gerade 16. Bei C kann ich alles Grundsätzliche und ein bisschen Speicherverwaltung. Außerdem machen wir in der Schule Java. Das, was wir gemacht haben, ist aber, bis auf die objektorientierten Elemente, praktisch genauso wie C (Schleifen, Bedingungen etc.).
Eigentlich interessiere ich mich schon für solche Sachen. Deshalb plane ich auch, evtl. Technische Informatik zu studieren, evlt. auch an der FH (bzw. Hochschule für angewandte Wissenschaften :D). C ist glaube ich Standard im Studium. Was ist noch wichtig? Und ja, mir ist klar, dass da sehr viel Mathe dabei ist. Deswegen sind diese Probleme wohl nicht schlecht.
 
Zurück
Oben