Modulo und Runden oder Abschneiden

Duda

Erfahrenes Mitglied
Hallo an die Community,

vorweg möchte ich mich auch auf diesem Wege nochmal bei Alex_T bedanken,
welcher mir über PM schonmal eine Frage zu Modulo sehr ausführlich und kompetent erklärt hat.
Doch ich bin ein totaler Neuling in C,C++ oder VB.
Mein Gehirn ist auch nicht mehr so ganz frisch, aber es funktioniert noch und das schon seit mehr als 70 Jahre.
Da ich leider die englische Sprache nicht so gut beherrsche, deshalb freue ich mich besonderst,
daß ich dieses Forum gefunden habe.
Deshalb schon mal danke an den Forumsbetreiber und an alle Mitglieder.
(Ein wenig Süssholz raspeln, das muß sein. :D:D:D)
Der Satz in Klammern, der wäre mir in Englisch schon schwer gefallen.
Okay, ein wenig Lesen, das geht so lala.
Doch nun zum Thema:
Ich glaube irgenwo gelesen zu haben,
daß Modulo nicht in jeder Programmiersoftware auch Modulo heißt,
aber das ist mir erstmal einerlei.
Ich habe gelesen, Modulo rundet was ab. Ich hoffe daß nichts an meinen Dezimal-Nachkommastellen gerundet wird.
Hierzu möchte ich gern wissen, ob mit dem Runden ein Auf- oder Abrunden der möglichen Nachkommastellen gemeint sind,
so wir es früher in der Schule gelernt haben oder wird hier grundsätzlich immer,
egal was hinter dem Komma(was in dem Fall ein Punkt ist) auf die ganze Zahl abgerundet oder auf eine Nachkommastelle.
Ich brauche diese einstelligen Dezimalbruchzahl für Berechnungen.
Habe mehreren englischen Erklärungen leider nichts entnehmen können, was mich schlauer gemacht hätte.

In deutscher Sprache habe ich leider nur ein Beispiel mit Minusvorzeichen gefunden.
Da wird abgerundet, siehe Beispiel: -0.1 auf -1, -1.1 auf -2, 2.1 auf -3.
Aber ich verwende keine Minuszahlen, deshalb nun endlich zu meinem Vorhaben.

Hier mein Beispiel, womit ich die Zahl 3 erzeugen möchte.

Was wie folgt ablaufen soll: 43 geteilt durch 10 sind 4.3 modulo 4 sind 0.3 mal 10 sind 3

Das Ganze in dieser Form (((43 / 0x10) %0x04) * 0x10)

ich hoffe, daß innerhalb der Klammern nichts gerundet wird, denn sonst komme nicht auf meine gewünschte 3.

Wobei es aber auch bei dem "Modulo unter anderem Namen" als Abschneiden bezeichnet wird, wobei ich auch hier gern
wissen möchte, ob grundsätzlich die Nachkommastellen abgeschnitten werden oder ob da zum Beispiel im Programm angegeben werden muß, in wie viel Stellen die Zahlen dargestellt werden sollen oder so.
Ja, ich weiß, daß das Ganze auch eine Compiler-Angelegenheit ist und man unterschiedliche Ergebnisse haben kann.
Wobei dies wahrscheinlich nicht CSS oder C++11 betrifft, weil hier ist vorgeschrieben, daß Ganzzahlen in Richtung 0 gerundet werden.
Aber es gibt doch sicherlich etwas ältere Compiler, wo festgelegt werden muß wie und was gerundet oder abgeschnitten werden soll.
Ist eventuell keine fachgerechte Beschreibung von mir und ich entschuldige mich für mein
langes und unübersichtliches Schreiben.
Für Antworten bedanke ich mich im Voraus.

Mir freundlichen Grüssen

Duda
 
Zuletzt bearbeitet:
Hallo Duda

Als erstes möchte ich dir kurz erklären was Modulo in C++ macht. Grundsätzlich ist das sehr einfach, in der Schule nannten wir das damals 'Dividieren mit Rest'. Genau das macht Modulo, es dividiert zwei Zahlen und gibt dir dann den Rest der Division. Per se funktioniert das in C++ nur mit Ganzzahlen (zumindest mit dem bereits erkannten %-Operator). Hier ein paar Bespiele dazu:
43 % 10 -> 43 / 10 = 4 Rest 3 -> 43 % 10 = 3
100 % 10 -> 100 / 10 = 10 Rest 0 -> 100 % 10 = 0
20 % 6 -> 20 / 6 = 3 Rest 2 -> 20 % 6 = 2

Etwas interessanter wird die Angelegenheit bei negativen Ganzzahlen. Dies jedoch auch nur wegen einem Detail, erklärbar an einem einfachen Beispiel:
-10 % 3

Möglichkeit 1:
-4 * 3 = -12 Rest 2 (-12 + 2 = -10) -> -10 % 3 = 2

Möglichkeit 2:
-3 * 3 = -9 Rest -1 (-9 + -1 = -9 - 1 = -10) -> -10 % 3 = -1

Tatsächlich tritt hier Möglichkeit 2 ein, -10 % 3 ist also -1

Einige weitere Beispiele:
-10 % 2 -> -10 / 2 = -5 Rest 0 -> -10 % 2 = 0
-10 % -3 -> -10 / -3 = 3 Rest -1 -> -10 % -3 = -1

Soweit mal die Erklärungen zum so genannten Modulo-Operator %. Wie vorhin erwähnt funktioniert dieser nur auf ganzen Zahlen. Schreibst du in C++ also zum Beispiel 3.3 % 3 so wirst du einen Fehler erhalten, denn der %-Operator ist nicht definiert für Gleitkommazahlen wie 3.3 eine ist.

Der nächste interessante Punkt ist das Präfix 0x. Dieses gibt an, dass die darauf folgende Zahl im 16er-System aufgefasst werden soll. Ich gehe davon aus, du weisst was das ist. Falls nicht hier ganz kurze Erklärung:
Die einzelnen Ziffern werden mit Potenzen von 16 multipliziert und nicht mit Potenzen von 10, also Beispiel:
111 im normalen Rechensystem: 1 * 100(= 10 * 10) + 1 * 10 + 1 * 1 = 111
111 im 16er-System: 1 * 256 (=16 * 16) + 1 * 16 + 1 * 1 = 273 im 10er System

Wenn wir uns nun deine Formel ansehen:
(((43 / 0x10) %0x04) * 0x10)

Gehen wir von innen nach aussen:
43 / 0x10 -> Beachte, dass 0x10 = 16 weil du das Präfix 0x davor hast und 10 im 16er System ist 1 * 16 + 0 * 1 = 16 im 10er System
=> 43 / 0x10 = 43 / 16 = 2 (werden zwei ganze Zahlen dividiert werden die Kommastellen einfach abgeschnitten)

Also können wir 43 / 0x10 ersetzen durch 2
-> (2 % 0x04) * 0x10 bleibt übrig
2 % 0x04 ist 2 % 4 (0x04 ist 16er System und heisst 0 * 16 + 4 * 1 = 4 im 10er System) und 2 % 4 ist 0 * 4 mit Rest 2 -> 2 % 4 ist 2

Damit vereinfacht sich unsere Rechnung weiter zu:
2 * 0x10
Wie oben erwähnt ist 0x10 wieder eine Zahl im 16er System und zwar steht sie für die Zahl 16 im 10er System. Das heisst im Endeffekt steht da:
2 * 16 = 32

Das Endergebnis deiner Rechnung ist also 32 und nicht wie erwartet 3.

Nun noch kurz zu "Rundung" bzw. abschneiden von Nachkommastellen:
Dividierst du zwei Ganzzahlen werden alle Nachkommastellen einfach abgeschnitten, einige Beispiele:
40 / 2 = 20 -> 20
40 / 3 = 13.333333333 -> 13
40 / 4 = 10 -> 10
40 / 5 = 8 -> 8
40 / 6 = 6.666666666 -> 6
40 / 7 = 5.71428571... -> 5

Wenn du bei einer Division die Nachkommastellen nicht verlieren möchtest musst du sie als Gleitkommazahlen dividieren. Davon bietet dir C++ zwei mögliche Typen an: float und double. Diese beiden unterscheiden sich (möglicherweise) durch die Grösse und Genauigkeit.

/Mathematischer Exkurs:
Warum haben Gleitkommazahlen überhaupt eine Genauigkeit? Warum kann man damit nicht alle Werte abbilden? Dazu kannst du dir merken, dass jedes float oder jedes double oder allgemein jeder Wert in der Programmierung eine gewisse Menge an Speichern bekommt. Das heisst er bekommt eine gewisse Menge an 0 und 1 zugeteilt die er irgendwie verwenden kann um das zu speichern was er will. Mit einer endlichen Menge an 0 und 1 kannst du aber offensichtlich auch nur eine endliche Menge an verschiedenen unterschiedlichen Werten speichern, egal wie du das schlussendlich anstellt. In der Mathematik heisst das, dass die Menge der möglichen Werte eines Speicherblocks abzählbar ist. Da die Gleitkommazahlen allerdings alle reellen Zahlen umfassen und diese überabzählbar sind kannst du mit einer abzählbaren Menge an möglichen Werten nur einen Teil davon effektiv abbilden. Je mehr Speicher du hast desto mehr sind möglich -> Der Typ ist genauer.
/Ende mathematischer Exkurs

Auch hierzu einige Beispiele:
40.0f / 2.0f -> 20.0f
40.0f / 3.0f -> 13.3334f (vielleicht, oder 13.3333f, das ist abhängig von dem, der den Compiler macht)
40.0f / 4.0f -> 10.0f
usw

Nun erinnern wir uns aber daran, was ich vorhin gesagt habe: Der %-Operator funktioniert nicht auf Gleitkommazahlen! Das heisst, das würde nicht funktionieren:
(((43.0f / 10.0f) % 4.0f) * 10.0f)

Während hier eigentlich (sollte der %-Operator auch Gleitkommazahlen unterstützen) 3 herauskommen sollte wirst du das gar nicht compilieren können.

Für Gleitkommazahlen gibt es grundsätzlich die Funktion fmod allerdings denke ich haben meine bisherigen Erklärungen die meisten deiner Fragen zu Präzision, Nachkommastellen und Modulo bereits abgedeckt.

Viele Grüsse
Cromon
 
Hallo Cromon,

danke für die nette Art und Weise, wie Du Dich meiner Fragenrei angenommen hast und mich an Deinem Wissen teilhaben läßt.
Deine informationsreiche und für mich gut nachvollziehbare Erläuterung könnte nicht besser sein.
Um nochmal mit meinen Worten etwas hervor zu heben.
Das mit dem Präfix "0x" war mir so nicht bekannt,
Ich hatte angenommen, daß dies nur Vorzeichen sind,
damit im Prozess erkannt wird "Achtung sind Zahlen zum Rechnen".
auch das mit der 16 Potenz für die Zahl 10 war eine Wissenlücke für mich.
Naja, bin ebend noch ein Unwissende in Bezug auf C,C++, VB etc..
Deshalb möchte ich gleich noch meine Gedanken kund tun.
Ich denke nun, daß ich in den Codezeilen, wo ich nicht mit der 16er Potenz rechnen möchte,
das heißt wo ich vorhabe mit einer 10er Potenz zu rechnen, da werde das "0x" nicht benutzen.
Bei 0x04, was ja schon dem 16er System entspricht oder ob nur 4, da wirkt es sich ja nicht wirklich aus, denn 4 bleibt 4 in diesem Fall.
Könnte ich so verfahren, um an meine erwünschten Berechnungsergebnisse zu gelangen.
Und noch ganz kurz, wie ist es mit dem Runden von Multiplikationsergebnissen,
denn werde ich eventuell auch Nachkommazahlen(Dezimalbrüche) erhalten.

VG

Duda
 
Zuletzt bearbeitet:
Hallo Duda

Grundsätzlich funktioniert deine Rechnung oben pauschal nicht. Wenn du in deinem Statement nur Ganzzahlen hast werden in jedem einzelnen Rechenschritt sofort die Kommastellen entfernt, also zum Beispiel hier:
(4 / 10) * 20 führt nicht wie erwartet zu (4 / 10) = 0.4 * 20 = 8 -> keine Stellen abschneiden, da keine Kommazahl ist, sondern: (4 / 10) = 0.4 -> abschneiden -> 0 * 20 = 0. Du erhälst also als Ergebnis 0 und nicht 8 auch wenn ja beim Endergebnis gar keine Nachkommastellen abgeschnitten werden müssten.

In deiner Rechnung entstehen bei Zwischenschritten auch wenn du das 0x-Präfix wegnimmst Kommazahlen, diese werden (da du überall nur Ganzzahlen verwendest) automatisch sofort abgeschnitten. Entsprechend bekommst du nicht das Ergebnis, das du erwartest (ausser du bist mittlerweile so in Übung, dass du das gleich merkst, dann erwartest du das ;)). Logisch, die erste Abhilfe, die man sich vorstellen kann: Ich rechne halt mit Kommazahlen, dann passiert das nicht! Also entsteht folgendes Statement:
(((43.0 / 10.0) % 4.0) * 10.0)

Nun werden ja alles Kommazahlen verwendet und auch die Rechenschritte sind dann als Ergebnis Kommazahlen. Nur haben wir nun ein anderes Problem. Der normale %-Operator weigert sich mit Kommazahlen zu arbeiten, mit anderen Worten: Du wirst einen Compilerfehler erhalten, dass % nicht auf double funktioniert. Es gibt dafür jedoch eine Funktion aus dem Standard: fmod. Diese Funktion nimmt zwei Fliesskommazahlen und gibt dir den Rest der Division aus:
fmod(43.0 / 10.0, 4.0) * 10.0

Dieses Statement liefert dir dann korrekt deine gewünschte 3 (als double).

Viele Grüsse
Cromon
 
Hallo Cromon,

möchte mich nochmal bei Dir bedanken. Deine Antworten waren mir eine große Hilfe.
Ich habe alles verstanden.
Werde die Nachkommastellen als nicht vorhanden berücksichtigen und dem entsprechend die anderen Zahlen auswählen. Dadurch entsteht das Problem erst garnicht.
Aber ich bin Dir trotzdem dankbar für die von Dir angebotenen Lösungsvorschläge.

Alle Zahlen, welche ich benutze werden aus den Hexcodes einer ASCII-Tabelle generiert.
Somit werden nur ganze Zahlen von 0 bis 255 verwendet.
Aber es ist nun mal so, wenn was geteilt wird, dann geht was zu Bruch,
auch wenn es nur Dezimalbrüche ist.
Da ich keine Ahnung hatte, wie man damit innerhalb eines Programmes umgehen muß,
deswegen meine Fragen.

Ich bin nun dank Deiner Hilfe(betr.: Präfixe, 16er System sowie Runden und Abschneiden) und auch der Hilfe von Alex_T(betreffend Modulo) erheblich weiter gekommen.
Denn mein Wissensstand in C, C++ oder so, war gleich Null.
Nun fühle ich mich schon leicht darüber.
Also nochmals vielen Dank und vielleicht bis bald mal wieder,
denn es gibt sicher noch viel zu fragen,
wenn man es nicht selbst im I-Net findet.
Denn nur fragen und Euere Zeit stehlen, das möchte ich auch nicht,
höchstens wenn mal wirklich not am Mann ist oder so.
In dem Sinne, macht es gut.

Viele Grüsse
Duda
 
Zuletzt bearbeitet:

Neue Beiträge

Zurück