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?
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
inta=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
inta=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... ;-)
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.
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.
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.