C++ kleinste Zahl ausgeben

Der Code ist meines Erachtens eine sehr unschöne Mischung aus C und C++. Man muss ja nicht zwingend meine zuvor vorgeschlagene, puristische Einzeiler-Lösung nutzen (die erfüllt den Wortlaut der Aufgabe auch nicht und sollte bloss dazu dienen, den Nutzen der Standardbibliothek aufzuzeigen), doch `math.h` ist kein C++-Header (das passende Äquivalent wäre `cmath`), den kompletten Math-Header zu inkludieren, wenn man bloss die numerischen Limits benötigt scheint mir unschön (INT_MAX wird in limits.h bzw. climits definiert), und den Grund für das Einlesen als String und die explizite Konvertierung in eine Zahl kann ich nicht erkennen. Eher im Gegenteil, die <<- und >>-Operatoren der IO-Streams sind durchaus aus gutem Grund polymorph überladen.

Eine alternative C++-Lösung mit expliziter Schleife wäre:
[src=cpp]#include <iostream>
#include <limits>

int main() {
int res(std::numeric_limits<int>::max());

while(std::cin.good()) {
int x;
std::cin >> x;
if(x <= 0) break;
res = std::min(res,x);
}
std::cout << "Minimum: " << res << std::endl;
}[/src]
Man beachte die while-Bedingung std::cin.good(). Schlägt das Einlesen des nächsten Tokens im Stream in eine int-Zahl fehl, wird das failbit gesetzt, tritt ein Lesefehler auf das badbit, ist der Stream zu Ende, das eofbit. Wenn eines dieser Bits gesetzt ist, gibt die good()-Methode false zurück und die Schleife bricht ab. Exception-Handling ist an dieser Stelle nicht nötig.

Alternative unter Verwendung eines std::istream_iterator<int>, jedoch ohne std::min_element:
[src=cpp]#include <iostream>
#include <limits>
#include <iterator>

int main() {
int res(std::numeric_limits<int>::max());

for(std::istream_iterator<int> it(std::cin); *it>0&&std::cin.good(); ++it) {
res = std::min(res,*it);
}
std::cout << "Minimum: " << res << std::endl;
}[/src]

Zu beachten ist bei beiden Beispielen, dass das Resultat ungültig ist, wenn nicht mindestens eine gültige Zahl eingegeben wird. Das könnte man verhindern, indem man res mit -1 initialisiert und dies sowohl in der Schleife als auch vor der Ausgabe überprüft. Alternativ könnte man vor der Ausgabe überprüfen, ob res noch std::numeric_limits<int>::max() enthält.
 
Zuletzt bearbeitet:
Jedenfalls hat er hier im Thread jetzt noch gelernt was man unter "Programmierstil" versteht und wie viele mehr oder weniger gute Möglichkeiten es gibt schon eine eher einfache Aufgabe zu lösen :).

Wo wir grad dabei sind: In C++11 müsste man in Zeile 8 auch folgendes schreiben können:
[src=cpp]for(auto it(std::cin); *it>0&&std::cin.good(); ++it)[/src]
 
  • Thread Starter Thread Starter
  • #23
Die ganzen Antworten zeigen mir, dass es viele Möglichkeiten gibt ein Problem zu lösen.

Ich habe mir jetzt noch diverses Lesematerial besorgt und versuche mich Stück für Stück in C++ zurecht zu finden.
 
ImHo gehts eh erstmal nur teilweise darum C++ an sich zu lernen. Zum großen Teil gehts auch darum sich das entsprechende Denken und die Themen die in fast allen Programmiersprachen vorkommen anzueignen, von Schleifen bis Speicherverwaltung.

Noch was. Wir haben alle deine Vorlage verwendet, aber in der Aufgabenstellung heißt es:
Schreibe eine Funktion, in der beliebig viele positive Zahlen eingelesen werden und die kleinste an die aufrufende Funktion zurückgegeben wird.
Du sollst gewiss eine Funktion schreiben und die innerhalb von main() dann aufrufen.
 
  • Thread Starter Thread Starter
  • #25
Das hast du richtig gelesen.

Ich habe es mal versucht und bin auf folgendes Ergebnis gekommen:

[src=cpp]#include <iostream>
#include <iomanip>

using namespace std;


int kleiner()
{
double input;
int min=INT_MAX;

while(true)
{

cout<<"Geben sie eine Zahl ein:";
cin>>input;
if(input==0) break;

if(input<min) min=input;

}

return min;
}

int main()
{

int ergebnis;

ergebnis=kleiner();

cout<<"Die kleinste Zahl ist:"<<ergebnis;

cin.get();

}[/src]


Unschön ist noch der Abbruch bei einer Minus Zahl. Sobald eine Minus Zahl ausgegeben wird bricht das Programm ab und gibt die entsprechende Zahl aus...
 
Das finde ich unnötig kompliziert, eine einfache Abfrage, ob der Wert kleiner ist und eine davon abhängige Zuweisung wäre besser. Anderenfalls wird res immer ein neuer Wert zugewiesen, der eben teilweise der alte res-Wert ist. Der Compiler würde das vermutlich ohnehin weg-optimieren, aber die Lesbarkeit vom Code wird dadurch unnötigerweise erschwert.

Du findest also, dass
[src=cpp]if(x < res)
res = x;[/src]

einfacher ist, als

[src=cpp]res = x < res ? x : res;[/src]

Der Compiler wird beides gleich übersetzen, wenn er nicht ganz schrottig ist. Davon abgesehen finde ich syntaktischen Zucker eigentlich immer ganz hilfreich, wenn man nicht unnötig tippseln möchte. Die Lesbarkeit wird mMn in keiner der beiden Möglichkeiten eingeschränkt.



Außerdem enthält Dein Code eine falsche Abbruchbedingung. Das Programm soll ja nicht beendet werden, wenn eine nicht-numerische Eingabe erfolgt, sondern wenn die Eingabe 0 ist. Da in der Aufgabe ausnahmslos genannt wird, dass Zahlen eingegeben werden, könnte man sich die Abfrage nach dem String ohnehin sparen.
Das habe ich wohl überlesen, allerdings sollte man zumindest beachten, was bei nicht nummerischen Eingaben passiert. Die verwendete Funktion wird dann nämlich eine Exception werfen und das sollte man durchaus beachten. Ob wie man darauf reagiert, bleibt dann jedem selbst überlassen.



Der Code ist meines Erachtens eine sehr unschöne Mischung aus C und C++. [...] doch `math.h` ist kein C++-Header (das passende Äquivalent wäre `cmath`), den kompletten Math-Header zu inkludieren, wenn man bloss die numerischen Limits benötigt scheint mir unschön (INT_MAX wird in limits.h bzw. climits definiert)
Ich habe schon seit Jahren nichts mehr mit C / C++ zu tun gehabt, zumal ich es immer nur stiefmütterlich behandelt habe. Ich hoffe man möge mir verzeihen.. ;)



und den Grund für das Einlesen als String und die explizite Konvertierung in eine Zahl kann ich nicht erkennen. Eher im Gegenteil, die <<- und >>-Operatoren der IO-Streams sind durchaus aus gutem Grund polymorph überladen.
Nun, was passiert denn, wenn im Zweifelsfall ein "a" und 4 verglichen werden bzw wenn man versucht ein int res = "a" zu setzen?
Zumindest aus der Java-Welt kommend würde ich daher zumindest eine Konvertierung durchführen, um sicher zu gehen, dass ich keinen Mist mache.




Wenn ich das noch richtig in Erinnerung habe, dann sollte int i = std::stoi(x); versuchen x in int umzuwandeln, schlägt das fehl, wird eine Exception geworfen.
Damit das Programm dann aber nicht komplett auseinander fliegt, fängst du mit catch (Exception e) die Exception wieder auf. Die Idee war, dass der Loop (der ja theoretisch unbegrenzt läuft) eben bei der Eingabe eines Nicht-Int-Wertes aufgebrochen wird und das Programm terminiert.
Sprich: Solange der Anwender Zahlen eingibt, speichert dein Algorithmus immer die kleinste Zahl, sobald eine Nicht-Zahl [genauer: ein nicht Integer] eingegeben wird, gibt der Algorithmus das Minimum aus.


Warum bei Negativzahlen eine positive Zahl ausgegeben wird, kann ich dir erklären, das hängt mit signed und unsigned Werten ab.

Wenn du unsigned 3 bit hast, dann kannst du darstellen:
(dual = dezimal)
000 = 0
001 = 1
010 = 2
011 = 3
100 = 4
101 = 5
110 = 6
111 = 7

bei signed 3 bit, kannst du darstellen:
(dual = dezimal)
000 = 0
001 = 1
010 = 2
011 = 3
100 = -1
101 = -2
110 = -3
111 = -4

Die führende 1 (also auf dem high bit) zeigt in dieser Darstellung an, dass es sich um eine negative Zahl handelt.

Wenn du an einer Stelle ein signed int verwendest, der eine führende 1 trägt, also eigentlich negativ ist und du an einer anderen Stelle mit unsigned int arbeitest, dann glaubst du eben an der zweiten Stelle du hättest eine riesige positive Zahl, während du an der anderen eben eine negative Zahl gespeichert hast.
 
Nun, was passiert denn, wenn im Zweifelsfall ein "a" und 4 verglichen werden bzw wenn man versucht ein int res = "a" zu setzen?
Zumindest aus der Java-Welt kommend würde ich daher zumindest eine Konvertierung durchführen, um sicher zu gehen, dass ich keinen Mist mache.
Das wird nicht geschehen, weil ein String in einer statisch und strikt typisierten Sprache wie C++ nicht in einer Variablen des Typs int abgelegt werden kann. Der >>-Operator ist polymorph überladen, die konkrete Operation hängt also vom Operand auf der rechten Seite ab - >> (int) liest eine Ganzzahl in eine int-Variable, >> (std::string) ein Wort ein einen std::string.
Wird ein Token eingegeben, der sich nicht in eine Ganzzahl umwandeln lässt, wird in
[src=cpp]int x;
std::cin >> x;[/src]
die Operation >> (int) fehlschlagen. In diesem Fall wird gemäss das failbit des Streams gesetzt. Konnte aus dem Stream aus anderen Gründen kein Token gelesen werden, wird das badbit gesetzt. Diese Bits sollte man spätestens abfragen (etwa über std::cin.good()), bevor man einen weiteren Wert aus dem Stream zu lesen versucht (denn das wird erneut fehlschlagen).
 
Das wäre doch eine schöne Übung, dass der TE das noch in sein Programm einbaut ;-).
noch ein Link der in dem Kontext interessant ist.
 
Abbruch bei Minus Zahl? Das kann ich nicht wirklich nachvollziehen. Oder versuchst du sowas wie -0.55 einzugeben? Dann ist es sogar logisch. -1 müsste er auf jeden Fall richtig erkennen.
 
  • Thread Starter Thread Starter
  • #30
Hey,

habe das ganze noch einmal geprüft:

bild.png

Scheint jetzt zu klappen, er rundet bei Komma Zahlen nur ab.
Negative Zahlen erkennt er aber auch als kleiner, da hast du recht!
 
Die Abrundung ist aber logisch, du hast schließlich min als Integer deklariert welches nur ganze Zahlen beinhaltet. Wenn du das Ganze auf Double abänderst hast du dann auch wieder die korrekten Nachkommastellen dabei stehen. Man muss nur aufpassen ob das Initilaisieren mit MAX_INT dann noch richtig hinhaut bzw. es nicht sowas wie MAX_DOUBLE gibt.
 
  • Thread Starter Thread Starter
  • #32
Ja der Fehler leuchtet mir auch ein und den unterschied zwischen integer und double wurde mir durch dieses Beispiel auch deutlich gemacht :)

Danke an alle für die Tipps!
 
Zurück
Oben