Hallo Forumsleser,
ich programmiere schon seit einiger Zeit in C, bin aber nicht der Crack
und bin nun einem Programmierproblem auf der Spur.
gegeben sei folgendes Beispiel:
byte GanzzahligerWert;
GanzzahligerWert = 100/(1+a/b);
wobei a und b Werte zwischen 1 und 50 annehmen kann. Ich vermute, dass
zunächst der Klammerausdruck berechnet wird. Im Falle von a=1 und b=50
wären das
GanzzahligerWert = 100/(1+1/50);
Kann es sein, dass der Klammerausdruck als Integer berechet wird und
deshalb (1+1/50)=1 ist und deshalb
GanzzahligerWert = 100/(1+1/50);
den Wert von 100 und nicht wie im Taschenrechner 98 hat?
Ich habe ein bißchen recherchiert, bin aber auf keine klare Bezeichnung
des Verhaltens gestoßen: implizite Typkonvertierung? Wie nennt sich das?
Besten Dank für Feedback
newbie12
newbie12 schrieb:> Kann es sein, dass der Klammerausdruck als Integer berechet wird und> deshalb (1+1/50)=1 ist
Ja, welche Datentypen habe a und b? Wenn es keine float/double sind,
führt der Compiler automatisch eine Ganzzahl Division aus. Wenn es
Integer sind, kannst du einen der beiden nach float/double casten um den
Compiler zu einer fließkomma Division zu zwingen.
newbie12 schrieb:> Sorry hatte ich vergessen a und b sind ebenfalls byte.
Ich vermute mal dass das eine 8bit breite Ganzzahl ist, dann würde es
das beschriebene Verhalten erklären.
Max H. schrieb:> newbie12 schrieb:>> Sorry hatte ich vergessen a und b sind ebenfalls byte.> Ich vermute mal dass das eine 8bit breite Ganzzahl ist, dann würde es> das beschriebene Verhalten erklären.
..ja, genau, byte hat einen Wertebereich von 0..255.
newbie12
Alternativ könntest du b*100/(a+b) rechnen. Dann gibts die Division erst
ganz am Ende und es kommt zumindest irgendwas anderes als 100 raus. Bei
deinem Beispiel mit a = 1 und b = 50 kommt bei mir auch nicht 98 raus
sondern 98.03921569...
Wenn du es als b*100/(a+b) hinschreibst kriegst du immer den
abgerunderen ganzzahligen Wert den der Taschenrechner anzeigt
(angenommen die Zwischenergebnisse werden nicht zu groß für int).
empfehlung: lies ein gutes Buch oder ein gutes Online-Tutorial und
arbeite solche Dingen durch. Wenn man in C programmieren will, dann
sollte man wissen was implizit hinter jeder Operation passiert.
Dann treten genau solche einfachen und leicht zu vermeidbaren Fehler
überhaupt nicht auf.
newbie12 schrieb:> Ich habe ein bißchen recherchiert, bin aber auf keine klare Bezeichnung> des Verhaltens gestoßen: implizite Typkonvertierung? Wie nennt sich das?
Das nennt sich Rechnen mit Ganzzahlen. Es wird dort gar nichts
konvertiert (außer vielleicht Integer Promotion weil du sagtest, dass du
mit Bytes rechntest. Wenn du mit Werten kleiner als int rechnest wird
das implizit zu einem int hochkonvertiert). Implizite Konvertierung gibt
es wenn die Operanden unterschiedliche Typen haben. Also z.B. ein float
und ein int. Dann wird implizit zu einem float konvertiert und man kann
einfach sowas wie 1.f/a schreiben wenn a ein int ist.
ffff schrieb:> empfehlung: lies ein gutes Buch oder ein gutes Online-Tutorial und> arbeite solche Dingen durch. Wenn man in C programmieren will, dann> sollte man wissen was implizit hinter jeder Operation passiert.>> Dann treten genau solche einfachen und leicht zu vermeidbaren Fehler> überhaupt nicht auf.
@all: Danke erstmal an alle für die Überlegungen.
@ Sebastian V. O.: Das mit dem Umformen habe ich auch gemacht.
Tatsächlich ändert sich das Ergebnis, wenn ich es so umforme, wie du
vorgeschlagen hast.
@ffff: So ein C-Tutorial/Buch ist groß. Ziel meiner Frage war, das
beschriebene Verhalten einer Begrifflichkeit zuzuordnen, die man im
Tutorial gezielt suchen kann. Es geht um die Reihenfolge der Abarbeitung
eine Kette von arithmetischen Operationen bei gleichzeitiger
Beschränkung des Wertebereiches.
Sebastian V. O. hat es gut herausgearbeitet:
GanzzahligerWert = 100/(1+a/b) und
GanzzahligerWert = 100*b/(b+a)
führen zu verschiedenen Ergebnissen. Vermutlich ist es so einfach, wie
Sebastian V. O. es bereits gesagt hat: "Das nennt sich Rechnen mit
Ganzzahlen". Ich vermute mal, dass das für jeden (längeren)
arithmetischen Ausdruck gilt: jeder Wert, der sich aus zwei Operanden
und einem Operator ergibt, hat den gleichen Wertebereich, wie die
Variable, der am Schluß zugewiesen wird.
Ich habe verstanden. Hoffentlich auch mein Compiler.
Dank und guten Abend
newbie12
newbie12 schrieb:> Ich vermute mal, dass das für jeden (längeren)> arithmetischen Ausdruck gilt: jeder Wert, der sich aus zwei Operanden> und einem Operator ergibt, hat den gleichen Wertebereich, wie die> Variable, der am Schluß zugewiesen wird.
Nein der Typ aus zwei Operanden mit Operator ergibt sich aus den Typen
der Operanden. Wenn die Typen unterschiedlich sind wird der Typ mit dem
"größeren" Wertebereich gewählt. Also bei einem int und long ist der
neue Typ ein long. Bei int und float gibts einen float. Wenn der
arithmetische Ausdruck fertig berechnet ist gibts noch eine implizite
Konvertierung zu dem Typ der Variable, welchem der Wert zugewiesen wird.
Das hier sieht nach einer einigermaßen ausführlichen Beschreibung aus:
http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/007_c_typumwandlung_001.htm
Sebastian V. O. schrieb:> newbie12 schrieb:>> Ich vermute mal, dass das für jeden (längeren)>> arithmetischen Ausdruck gilt: jeder Wert, der sich aus zwei Operanden>> und einem Operator ergibt, hat den gleichen Wertebereich, wie die>> Variable, der am Schluß zugewiesen wird.>> Nein der Typ aus zwei Operanden mit Operator ergibt sich aus den Typen> der Operanden. Wenn die Typen unterschiedlich sind wird der Typ mit dem> "größeren" Wertebereich gewählt. Also bei einem int und long ist der> neue Typ ein long. Bei int und float gibts einen float. Wenn der> arithmetische Ausdruck fertig berechnet ist gibts noch eine implizite> Konvertierung zu dem Typ der Variable, welchem der Wert zugewiesen wird.>> Das hier sieht nach einer einigermaßen ausführlichen Beschreibung aus:> http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/007_c_typumwandlung_001.htm
besten Dank, auch für den Buchtipp und die Kategorie "Typumwandlung".
Newbie12
newbie12 schrieb:> hat den gleichen Wertebereich, wie die> Variable, der am Schluß zugewiesen wird.
Nein.
Das ist ein beliebter Fehler, der oft für Verwrrung sorgt.
Dafür ist nur die rechte Seite vom = wichtig.
Bei der Berechnung gelten die normalen Regeln wie Klammerung, Punkt vor
Strich, ...
Und es wird nur die für die jeweilige Operation notwendige Umwandlung
gemacht.
Bei 1.5 + 1/2 kommt 1.5 raus (double), da erstmal 1/2 als Integerdivison
berechnet wird.
python2 rechnet ganzzahl wenn keine floats drinne
python3 rechnet einfach so float wenn er meint es muss sein
total verwirrend und absolut off topic :)
>empfehlung: lies ein gutes Buch oder ein gutes Online-Tutorial und>arbeite solche Dingen durch. Wenn man in C programmieren will, dann>sollte man wissen was implizit hinter jeder Operation passiert.>Dann treten genau solche einfachen und leicht zu vermeidbaren Fehler>überhaupt nicht auf.
Super konstruktiver Vorschlag. In der Zeit hättest Du die Lösung
präsentieren können :-(
Warum nimmst Du nicht float als Datentyp für a und b?
Rosa
Rosa schrieb:> Super konstruktiver Vorschlag.
Der Vorschlag ist konstruktiv. Selbst lernen. Nicht eine Antwort
vorbeten lassen, ohne zu verstehen.
Aber das ist in der Generation facebook/google offensichtlich nicht zu
vermitteln.
> Warum nimmst Du nicht float als Datentyp für a und b?
Versuch mal darüber nachzudenken, was das bedeutet. Wie heißt diese
Webseite hier? "mikrocontroller.net".
Und welche Auswirkung kann die Verwendung von float auf einem
Mikrocontroller haben? Na?
Es ist doch auch eine Frage, ob auf Seiten der Anwendung etwas dagegen
spricht. Mehr habe ich nicht gefragt.
Ich glaube, du hast meine Antwort persönlich genommen. Entschuldige
bitte, das kommt nicht wieder vor.
Rosa
Ps: ich glaube schon zu wissen, was es bedeutet, float zu nutzen.
Pps:Vorführen und Nachmachen ist nicht mal die schechteste Methode,
schlechter ist ein Buch, das ich nicht verstehe.
python schrieb:> python2 rechnet ganzzahl wenn keine floats drinne> python3 rechnet einfach so float wenn er meint es muss sein>> total verwirrend und absolut off topic :)
Verwirrend in Python 2 ist wie auch in C vor allem die Tatsache, dass
der /-Operator je nach Argumenttypen für zwei völlig unterschiedliche
Operationen steht. Denn die ganzzahlige Division ist im mathematischen
Sinn gar keine Division, da sie nicht als Umkehrung der Multiplikation
definiert ist.
Während in C diese Dimorphie lediglich ein Stolperstein für Anfänger
darstellt, ist sie in Python 2 eine generelle Fehlerquelle. Deswegen
liefert / in Python 3 auch für Integer-Argumente ein Float-Ergebnis mit
den entsprechenden Nachkommastellen. Für die ganzzahlige Division gibt
es den Operator //.
Evtl. wäre es auch bei der Sprachdefinition von C klüger gewesen, zwei
verschiedene Operatoren für die beiden Divisionsarten vorzusehen.
Womit wir auch schon fast wieder ontopic wären ;-)
Yalu X. schrieb:> Evtl. wäre es auch bei der Sprachdefinition von C klüger gewesen, zwei> verschiedene Operatoren für die beiden Divisionsarten vorzusehen.>> Womit wir auch schon fast wieder ontopic wären ;-)
Und noch etwas mehr Off-Topic:
Da würden Mathematiker wohl etwas dagegen haben...
Das, was C da macht, ist ganz normale Arithmetik über einem
Restklassenring bzw. Faktorring und hat den schönen Nebeneffekt, dass
bspw., falls das multiplikate Inverse existiert, eine Division durch
eine Multiplikation mit dem Inversen berechnet werden kann.
Arc Net schrieb:> Da würden Mathematiker wohl etwas dagegen haben...
Ich bin zwar selber kein Mathematiker, könnte mir aber vorstellen, dass
gerade Mathematiker eine klarere Unterscheidung zwischen der echten und
der ganzzahligen Division befürworten würden.
Gerade in den funktionalen Programmiersprachen, die prinzipiell etwas
mathematischer angehaucht sind als andere, wird diese Unterscheidung
praktisch immer gepflegt.
> Das, was C da macht, ist ganz normale Arithmetik über einem> Restklassenring bzw. Faktorring und hat den schönen Nebeneffekt, dass> bspw., falls das multiplikate Inverse existiert, eine Division durch> eine Multiplikation mit dem Inversen berechnet werden kann.
Solche Späße wären aber doch auch möglich, wenn der Operator für die
ganzzahlige Division ander als / heißen würde, oder?
python schrieb:> python3 rechnet einfach so float wenn er meint es muss sein
Was natürlich Unsinn ist.
Python 3 ist sehr viel konsistenter als Python 2, was das
Divisionsverhalten angeht. Siehe / vs. //