Zähler Variablen immer als Int deklarieren?

  • Guten Abend,


    auf einem Arbeitsblatt, welches wir heute im Unterricht bekommen haben, stand drauf das Zähler Variablen möglichst immer Int sein sollten.


    Warum ist das so ? Ist das nur ein Stil Mittel oder hat das noch einen anderen (wichtigen) Grund ?

  • Ich würde nicht sagen „damit sie das nur kann“, sondern „weil sie das nur braucht“. Man sollte Ressourcen darauf begrenzen, was wirklich benötigt wird. Eine Zählervariable muss halt nicht Dinge wie 6,283185 kennen.

  • Na ja, aber mit float stellst du ja nicht nur Q dar, sondern ggf. auch R...


    Anyway - das Problem ist, dass float in einem Koprozessor behandelt wird, nicht im Hauptprozessor. Sobald eine Variable der Berechnung ein float Wert ist, wird der Rechenschritt als float-Operation gehandhabt und findet dann entsprechend im Koprozessor statt.


    Damit würde man plötzlich für eine simple Operation wie das Aufaddieren einer eins unnötig den float-Koprozessor in Anspruch nehmen und somit wertvolle Ressourcen verfeuern.

  • Mir fällt es grad kein Grund Zählvariablen als float zu deklarieren ein. Wobei ich auch noch nie gesehen habe dass sowas (in Zählschleifen) möglich ist.


    Denn wie groß soll der Zählschritt denn sein? wenn er 0.01 groß sein soll, dann teilt man die Zählvariable halt immer durch 100, wenn er 0.1 groß sein soll dann durch 10.
    Da du zu einem float Zähler somit auch immer die Schrittweite angeben müsstest, ist es nicht kürzer oder sinnvoller als einen int-Zähler zu verwenden und den dann abhängig vom Zählschritt zu teilen.

  • Man macht ja immer eine Kosten/Nutzen Rechnung.


    Sagen wir du möchtest eine C++ for-Schleife, die dir jedes mal 0.01 auf die Laufvariable aufaddiert und anschließend diesen float-Wert verwendet um damit irgendwas anzustellen. Sagen wir man möchte diese Werte einfach nur ausgeben:


    Code
    1. for (float i = 0.0f; i < 1; i += 0.01){ cout<<i<<endl;}


    Die Alternative wäre:


    Code
    1. for (int i = 0; i < 1; i++){ cout<< i / 100.0f<<endl;}


    Ihr habt nun einmal einen float-Wert als Laufvariable, entsprechend wird jedesmal beim Aufaddieren eine kostspieleige float-Operation auf dem Koprozessor genutzt. Das müsst ihr bei der zweiten Variante nicht machen, aber stattdessen müsst ihr in jedem Aufruf die Division durch 100 durchführen um das Ergebnis zu erhalten.


    Diese Division auf dem Integer Bereich liefert euch aber 0. Man muss also mit dem .0f, oder aber mit einem richtigen Cast einen der Operanden in einen float-Wert überführen: i/(float)(100) oder i/100.0f


    Man hat also so oder so in jedem Schritt eine float-Operation, daher wäre es in diesem Beispiel egal welche Variante man nutzt (bzw. man müsste dann sogar eher noch schauen ob die Division nicht kostspieleiger ist, das weiß ich nicht aus dem Kopf).



    Sagen wir ihr habt sowas:




    In diesem Beispiel haben wir eine alternierende boolsche Variable, also einen true/false Wert. Hier würde es keinen Sinn machen, for(float i = 0.0f; i < 0.1; i += 0.01) zu verwenden, denn die teure float-Operation bräuchten wir nur bei jedem zweiten Aufruf. In diesem simplen Beispiel kann man das natürlich modellieren, indem man statt i += 0.01 einfach i += 0.02 verwendet, aber es gibt ja viel komplexere Geschichten. Man könnte ja aus der Schleife heraus eine Methode doMagic(i) aufrufen, die anhand der aktuellen Laufvariable irgendeine komplexe Berechnung durchführt und bei bestimmten Resultat die boolean_value setzt, schließlich kann es irgendein globaler Wert sein, oder man übergibt ihn an die Funktion, da gibt es endlose Möglichkeiten.


    Daher sollte man mit float-Laufvariablen vorsichtig sein. Es gibt also schon Fälle, wo man sie verwenden kann, aber ich denke 99% der Schleifen verwenden Integer, weil es einfach sinnvoller ist, nicht aus Stilgründen.

  • Ein viel größeres Problem, als der unterschiedliche Ressourcenverbrauch ist mMn, dass floats (und doubles) oft nicht exakt dargestellt werden und es beim Rechnen zu kleineren Rundungsfehlern kommt.
    Das ist natürlich unter Umständen gar nicht toll, z.B. wird diese Schleife hier einmal zu oft durchlaufen.


    Code
    1. for (float i = 0.0f; i < 3; i += 0.1) { System.out.println(i);}


    output:

  • Sieht für mich eher nach einem Java/Compiler-Problem aus, mein Output für diese Schleife in C++ ist:



    Grundsätzlich hast du aber recht, bei float-Werten muss man mögliche Rundungsfehler berücksichtigen.


    Passiert die aber bei einer vernünftigen Sprache mit i += 0.01 eher nicht. :p


    Aber wir reden ja nicht nur von banalen Situationen.



    EDIT: Achso, du meinst die Tatsache, dass die 3 gar nicht auftauchen dürfte am Ende? Ging von <= 3 aus, aber wer von irgendetwas ausgeht failed bekanntlich rum. :p


    Ich meinte die Rundungsfehler in den Nachkommastellen.


    Kann man davon ausgehen, dass diese im Koprozessor so oder so bestehen und lediglich vom Compiler jeweils unterschiedlich behandelt werden können? Sodass zB der Koprozessor jeweils denselben Wert geliefert hat, aber der C++ Compiler dennoch eine entsprechende Interpretation anbieten konnte? Reine Neugier, entschuldigt wenn das zu sehr abschweifen sollte.

  • Nö, jeder Bruch, der sich mit float darstellen ließe, lässt sich auch mit int darstellen. Aber auf deinem Arbeitsblatt steht ja auch nicht "immer", sondern "möglichst immer".


    Naja genau genommen steht dort schon "immer". Hatte gerade nochmal nachgeguckt.
    Mir war die Begründung mit den Ressourcen eigentlich schon immer klar, aber ich hatte ansich rein aus Interesse gefragt ob es vlt irgendwo Sinn mach auch mit kommazahlen hoch zu zählen.


    Ich hab noch keine besonderen Programmier Erfahrungen

  • Mir würde nichts einfallen wo es zwingend notwendig wäre.


    Er fragte ja auch ob es irgendwo Sinn macht ;)
    Ich würde sagen, dass man es machen kann, wenn der Vergleichswert auch eine float-Variable ist.
    Z.B.

    Code
    1. float f = 0.0;
    2. float x = 2.1;
    3. for(f; f < x; f += 0.1){ }


    Wie oft sowas in der Praxis vorkommt, sei mal dahingestellt.

  • [...]


    Das Ganze ist ein Problem des Datentyps und nicht der Programmiersprache. Viele endliche dezimal-Kommazahlen werden in Binärschreibweise periodisch. In dem Fall muss das letzte bit deiner Variable gerundet werden. Beim Zurückrechnen in Dezimalschreibweise kommt es dann zu Abweichungen vom gewollten Wert.
    Deswegen kann (sollte) man auch Gleitkommazahlen nicht mit a==b auf Gleichheit prüfen, sondern irgendwie im Sinne von |a - b| < ε .


    Ich wüsste nich, was dein Compiler dagengen machen könnte. Deine Werte sind wahrscheinlich genauso ungenau wie meine, bloß deine cout Funktion hat ne geringere Genauigkeit :)


    Versuch mal, ob bei <3.0 als Abbruchbedingung nicht trotzdem 3.0 ausgegben wird.
    Außerdem, setz mal die Genauigkeit mit cout.precision(7); hoch und lass dann bis 10000 zählen oder so, dann bekommst hoffentlich auch krumme Werte angezeigt ;)

  • Quote

    Der Vollständigkeit halber: Nicht nur in C++ sondern wohl in fast jeder Sprache.


    Wohl eher: In fast jeder heute gängigen Sprache. Gibt wohl mehr Sprachen, die solchen 'syntactic sugar' nicht kennen, als solche, die's tun.

    Democracy is a device that ensures we shall be governed no better than we deserve.
    Patriotism is your conviction that this country is superior to all other countries because you were born in it.
    ~G. B. Shaw