Hallo in einer Funktion, die mir einen float zurückgibt, habe ich einfach direkt 2 Zahlen eingegeben: float xxx() { return 537/6 } das ergibt normalerweise 89.5, aber das Resultat is 89. Ich bin in die Falle getappt und habe zuerst auf Anhieb nicht sagen können, was das Problem ist, aber dann ist mir eingefallen, dass ja C im Hintergrund mit int's rechnet. Wenn ich 537/6.0 oder 537.0/6 rechne stimmt es. Was passiert nochmals genau im Detail? Ich habe das hier vor kurzem irgendwo gelesen, wo es Falk so schön erklärt hat (irgendwas mit: C rechnet von rechts nach links), aber ich finde diesen Thread leider nicht mehr. Dort hat bei jemandem auch die einfachste Rechnung nicht funktioniert und zwar genau aus diesem Grund oben. Könnte mir jemand kurz erklären, wie C da genau vorgeht?
Du teilst einen Int durch einen Int, warum sollte da für den Compiler was anderes als wieder ein Int rauskommen?
Berechnungen erfolgen immer im höchsten Format der beiden Operanden bzw. mindestens in int, der Zieltyp spielt keine Rolle. Ein Konversion erfolgt erst nach der Berechnung. Eine Ausnahme sind Schiebeoperatoren, da erfolgt die Berechnung im Typ des ersten Operanden.
Das ist wie in der Grundschule. Dort ergeben solche Divisionen ein Ergebnis und einen Rest. So rechnet der Computer auch - es sei denn, einer der Operanden ist eine Fließkommazahl. Ein verwandtes Problem bekommst du bei großen Zahlen. Ich gehe jetzt mal von einem AVR Mikrocontroller aus, wo die int Typen 16bit groß sind:
1 | int a=10000 * 8 / 100; |
Da kommt etwas völlig falsches bei heraus, weil "10000 * 8" mit einem Algorithmus für int nicht korrekt berechenbar ist. Das Ergebnis ist nämlich zu groß. Die Division durch 100 wird erst danach durchgeführt, aber nach dem kaputten Zwischenergebnis wird es nicht mehr besser. Man löst das so:
1 | int a=10000L * 8 / 100; |
Das L sagt dem Compiler, dass diese Zahl ein long Integer sein soll. Dann wendet er einen Algorithmus für Long Integer an, und der kann "10000 * 8" korrekt berechnen.
Peter D. schrieb: > Ein Konversion erfolgt erst nach der Berechnung. Danke, das wollte ich wissen. Peter D. schrieb: > Eine Ausnahme sind Schiebeoperatoren, da erfolgt die Berechnung im Typ > des ersten Operanden. Also: zB 537.0 / 128 >> 2 = 537.0 / 64 = wäre das Ergebnis also ein float, aber dann kann ich es ja gleich so schreiben, dass der erste oder 2. Operand einen Dezimalpunkt hat. 537.0/64 oder 537/64.0 Oder was genau ist mit dem Schiebeoperator gemeint? Wo kann ich künftig solche Informationen in schriftlicher Form nachschlagen? Einen C-Standard habe ich nicht, kostet die denn etwas? Wenn ja, was macht ein Privatnutzer wie ich dann?
Stefanus F. schrieb: > Das ist wie in der Grundschule. Dort ergeben solche Divisionen ein > Ergebnis und einen Rest. So rechnet der Computer auch - es sei denn, > einer der Operanden ist eine Fließkommazahl. > > Ein verwandtes Problem bekommst du bei großen Zahlen. Ich gehe jetzt mal > von einem AVR Mikrocontroller aus, wo die int Typen 16bit groß sind: > int a=10000 * 8 / 100; > > Da kommt etwas völlig falsches bei heraus, weil "10000 * 8" mit einem > Algorithmus für int nicht korrekt berechenbar ist. Das Ergebnis ist > nämlich zu groß. > > Die Division durch 100 wird erst danach durchgeführt, aber nach dem > kaputten Zwischenergebnis wird es nicht mehr besser. > > Man löst das so:int a=10000L * 8 / 100; > > Das L sagt dem Compiler, dass diese Zahl ein long Integer sein soll. > Dann wendet er einen Algorithmus für Long Integer an, und der kann > "10000 * 8" korrekt berechnen. Super Danke, das hilft mir. Gute Erklärung. Jetzt würde ich noch gerne das mit dem Schiebeoperator klären und die paar Fragen die ich oben gestellt habe und das wars dann auch schon.
jemand schrieb: > Oder was genau ist mit dem Schiebeoperator gemeint? Er meinte solche Situationen:
1 | PORTD = PINB << 1; |
Hier wird Der Port B eingelesen und alle Bits um 1 Position verschoben auf Port D ausgegeben. Die 1 ist ein Integer, das Ergebnis der Shift-Operation ist aber ein Byte, weil der linke Operand (PINB) ein Byte ist. Wo kann ich künftig solche Informationen in schriftlicher Form nachschlagen? Was den Standard angeht, schaue ich dort nach: https://de.wikibooks.org/wiki/C-Programmierung Da ich aber ausschließlich den gcc verwende, ist dieses Dokument für mich die erste Anlaufstelle: https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html Wo das hier diskutierte Verhalten dokumentiert ist, weiß ich allerdings nicht. Da hatte ich so wie du durch überraschende Effekte kennen gelernt.
jemand schrieb: > Oder was genau ist mit dem Schiebeoperator gemeint? Wenn du einen (long)24 um einen char(2) verschiebst, dann ist das Ergebnis logischerweise ein long Wert. Wenn du aber einen (char)24 um (long)2 verschiebst, dann ist das Egebnis eben kein long sondern bleibt ein char, obwohl der zweite Operator ja einen größeren Wertebereich hat. EDIT: Hoppla, nur Zweiter... ;-)
:
Bearbeitet durch Moderator
jemand schrieb: > Einen C-Standard habe ich nicht, kostet die denn etwas? Wenn ja, was > macht ein Privatnutzer wie ich dann? Die Google-Suche "C Standard" spuckt als erstes Wikipedia aus: https://de.wikipedia.org/wiki/C_(Programmiersprache) Dort findest Du einige Hinweise zu den C-Standards, die sich historisch von K&R-C bis C18 entwickelt haben. In den Fußnoten dazu findest Du Links auf die ISO-Standards. Wenn Du diesen folgst, wirst Du sehen: ja, die kosten etwas. Hol Dir lieber ein gutes Buch zu C, da hast Du mehr davon. Dein geschildertes Problem im Ausgangsposting ist absolutes Basic, was schon im K&R aus den 70ern erklärt wird.
:
Bearbeitet durch Moderator
jemand schrieb: > Einen C-Standard habe ich nicht, kostet die denn etwas? Ja, die kosten was > Wenn ja, was > macht ein Privatnutzer wie ich dann? Er schlägt im Draft nach. Das ist die vorletzte Version, die ohne Änderung als Standard übernommen wird. Die kann man im Netz finden. Allerdings stehen die Sachen da oft nicht so leicht verständlich drin. Bemerkung: Bei 537/6 * 3.5 wird die Division auch in int durchgeführt Die Rangfolge der Operatoren (u.a. Punkt vor Strich) findet man auch im Netz oder im Buch.
Ich verweise mal auf https://www.c-plusplus.net/forum/topic/300567/linkliste-f%C3%BCr-neulinge Ok, C18 gibt es auch schon.
jemand schrieb: > Könnte mir jemand kurz erklären, wie C da genau vorgeht? Vollständig, aber in Englisch, gibt es daß hier: https://en.cppreference.com/w/c/language/conversion Oliver
jemand schrieb: > zB 537.0 / 128 >> 2 = 537.0 / 64 = wäre das Ergebnis also ein float, Nein, double. jemand schrieb: > float xxx() > { > return 537/6 > } > > .... > > Wenn ich 537/6.0 oder 537.0/6 rechne stimmt es. Bei Fließkommazahlen wird mit "double" gerechnet. Willst du eine Fließkommazahl als "float" deklarieren, so musst du ein "f"/"F" anhängen: 537.0f (das ist ist eine float-Zahl, vorher war wie vom Typ "double"). double = doppelte Genauigkeit. So rechnest du in der Funktion und gibt die Zahl aber mit einer einfachen Genauigkeit zurück.
1 | printf("sizeof(%f)=%zd\n", 10.5, sizeof(10.5)); |
2 | printf("sizeof(%ff)=%zd\n", 10.5f, sizeof(10.5f)); |
Ausgabe:
1 | sizeof(10.500000)=8 byte |
2 | sizeof(10.500000f)=4 byte |
double und float ergibt bei AVR allerdings ausnahmsweise das gleiche, weil der avr-gcc nur float kann.
Bei Funktionen mit variablen Argumenten (z.B. printf) wird bei Fließkommazahlen mindestens double übergeben. Bei Ganzzahltypen mindestens int.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.