float Variable gibt bei Division einen int zurück (C#)?
Hey,
Ich bin momentan daran einen kleinen Snake-Klon in Unity als Lernprojekt zu entwickeln, um meine Grundkenntnisse und Überlegungen zu festigen. Dabei wollte ich ein ganz simples Schwierigkeitssystem entwickeln, welches die Zeit zwischen den Bewegungen der Schlange kontrolliert. Also je schwieriger, desto kürzer die Zeitabstände.
Ich wollte das machen, indem ich 10 durch die Quadratzahl einer difficulty-Variable teile, damit die Schwierigkeit proportional zunimmt.
int difficulty = 4;
float timerReset = 10 / (difficulty*difficulty);
Nun zum eigentlichen Problem: die Variable die die Zeitabstände kontrolliert ist ein float, doch wenn ich die oben zu betrachtende Berechnung eingebe bekomme ich einen int Wert zurück.
In diesem Beispiel wäre timerReset nach der Berechnung 0, obwohl es im Grunde genommen 0.625 ergeben sollte.
Kann mir da jemand weiterhelfen, wäre sehr dankbar darüber?
5 Antworten
Das liegt an der Art und Weise, wie C# intern arbeitet.
Es wird nicht erst die Variable als float deklariert und dann berechnet, sondern umgekehrt: Erst wird berechnet und danach die Variable deklariert und zugewiesen.
Das heißt, deine Berechnung sieht effektiv "int / (int * int)" und das Ergebnis davon ist auch ein int. Erst danach wird die Variable zugewiesen.
Du hast also mehrere Möglichkeiten: Mach die "difficulty"-Variable als float, oder rechne "10F" durch das Quadrad von "difficulty". Das "F" hinter der 10 sagt dem Compiler ("D" für double), dass die 10 als float betrachtet werden soll und nicht als int. Oder Du castest in der Berechnung auf float.
Immer wenn ein Teil einer Berechnung einen anderen Typ hat, als der andere Teil, versucht C# eine implizite Konvertierung. Bedenke dabei aber, dass auch die Berechnung aus mehreren Schritten bestehen kann. In deinem Fall wäre das erst die Multiplikation (da sie in Klammern steht), danach die Division. Das sind zwei Rechenschritte, für die die gleichen Regeln gelten.
oder rechne "10F" durch das Quadrad von "difficulty". Das "F" hinter der 10 sagt dem Compiler ("D" für double), dass die 10 als float betrachtet werden soll und nicht als int.
Danke, wieder was gelernt. 😉
Auf der Rechten Seite der Gleichung agiert Du Ausschließlich mit Intergerwerten. (Die Konsrante 10 wird implizit als Integer verwendet und difficulty ist als solcher deklariert. Das Ergebnis ist folglich ein Integer . Dieser Integer wird anschließend an eine float-Variable übergeben und erst zu diesem Zeitpunkt in einen float konvertiert.
Die var-Lösung von @GuteAntwort2021 funktioniert, kann sich jedoch im weiteren Programmverlauf zum Kuckucksei entpuppen:
var difficulty = 4.0;
var timerReset = 10 / (difficulty*difficulty);
//100 Zeilen später 🤮
float andereVariable=timerReset;
Peng: error CS0266: Der Typ 'double' kann nicht implizit in 'float' konvertiert werden.
(var difficulty = 4.0; und damit auch daraus resultierende Ergebnisse sind implizit ein double und passt nicht (ohne explizite Konvertierung in einen float !)
var sollte man sehr bedacht verwenden.
In diesem Fall würde ich konsequent mit double arbeiten, damit stellst Du sicher, das der Compiler durchgehen code für die Flieskommaeinheiten des Prozessors erzeugt und nich "Unterwegs" Implizite TypUmwandlungen vornehmen muss.
double difficulty = 4;
double timerReset = 10.0 / (difficulty*difficulty);
//100 Zeilen später!!!
double andereVariable=timerReset;
...so vermeidest Du "Unerwartetes", weil durchgehend klar womit Du arbeiten möchtest.
Hach ja, die schöne Debatte: var Ja oder nein :D
Das Problem, was Du oben skizzierst, tritt ja nur auf, weil Du nicht konsequent var benutzt. Wäre die dritte Variable auch var, gäb's das Problem nicht, dann würde er weiter mit Double rechnen.
Außerdem ist der Fehler ein Compile-Fehler und frisst als solches mMn. keine unnötige Zeit, wie es z.B. bei einem Laufzeit-Fehler der Fall wäre. Man sieht ja sofort, dass es da ein Problem gibt und wer über ein paar Stunden Erfahrung mit C# hinaus ist, hat auch ziemlich schnell eine Vermutung, was das Problem ist und wie man es fixen kann.
Umgekehrt habe ich die Erfahrung gemacht, dass die explizit angegebenen Typen sogar sehr viel häufiger zu unnötigen Compile-Fehlern führen, als das mit var der Fall wäre. Ich habe zu dem Zweck mal ein komplettes Projekt mit dem "kein var"-Ansatz aufgebaut und es hinterher bereut.
Ich denke, die Abneigung zu var rührt eher von anderen Problemen her, die man mit expliziter Typ-Angabe zu kaschieren versucht. In deinem Beispiel wäre das z.B. die Mischung von var und kein var und die Tatsache, dass da 100 Zeilen Code zwischen sein sollen. Wäre die Methode kompakter, wäre auch das Problem sehr viel weniger relevant, da man den Code leicht überblicken kann. Und die Offensichtlichkeit des verwendeten Typs ist mit aussagekräftigen Namen auch nicht mehr so relevant.
C# ist klar typisiert. Dadurch hat es eine deutlich schnellere Laufzeit als Scriptsprachen. Allerdings kannst du sowas machen:
var difficulty = 4.0;
var timerReset = 10 / (difficulty*difficulty);
Dann wird das Ergebnis automatisch zu einem double. Wenn du die Variable allerdings mit 4 statt 4.0 deklarierst, wird es eben als int gehandhabt.
So toll die automatische Typisierung mit var auch sein mag, man sollte es wirklich nur verwenden, wenn auf der rechten Seite einer Zuweisung der Typ explizit festgelegt wird. (auch anonyme Typen sind so gesehen explizit als solche festgelegt)
var difficulty = 4.0f; //rechte seite explizit eine float-Konstante
var xs = new List<int>(); //rechte seite explizit eine Liste von Integern
var v = new { Name="Franz",Age=78 }; ////rechte seite explizit ein Anonymous
...sonst kann var zur "Zeitbombe" werden.
weil deine erste variable ein int ist. diese muss auch ein float oder double sein
in python hätte es funktioniert aber in niedrigen sprachen geht sowas nicht
"Niedrigen" Sprachen. Solch einen Kommentar kann man wirklich nur als peinlich deklarieren.
Es ist tatsächlich die Frage hier was man überhaupt als höher definiert.
Im wesentlichen nennt man ja alle Sprachen über Assembler Hochsprachen so ist zB C++ in dem Sinne keine Hochsprache zu C auch wenn es ein Superset ist.
Weil alle beiteiligten Operanden (10 und difficulty=4) Ganzzahlen sind, verwendet C# für die Division auch die Ganzzahl-Division (Division ohne Rest). Das Ergebnis ist offensichtlich 0 und wird dann implizit von int nach float gecastet, um in timerReset geschrieben zu werden.
Du könntest entweder einfach direkt 10.0/... schreiben, die Difficulty direkt als float anlegen, vor der Division zu float casten und bestimmt noch Vieles mehr.
Das wusste ich tatsächlich nicht, aber demnach vielen Dank, werde es probieren!