Computer rechnen komisch?

Tommentator  31.07.2023, 01:26

Was heißt "nicht die richtige Lösung"?
Wie groß sind die Werte von Entfernung?

Guenfrgen1231 
Fragesteller
 31.07.2023, 10:51

Entfernung =756;

also soll die richtige Lösung 47,85. Da ich sie als int speichere wird sie auf 48 gerundet.

Und die nicht richtige Lösung ist 1038.

7 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

hmm..

int = ganzzahl

wenn du 1000/794 teilst bekommst du aber eine kommazahl.

du musst dein code so umschreiben, dass dieser entweder in ganzzahlen rechnet oder kommazahlen.

versuchs mal mit dem zahlentyp float oder double

Also ich hab's mal testweise in Java nachgemacht, die Sprache ist ja egal.

Ich krieg' bei deiner beschriebenen Vorgehensweise halt negative Werte raus, weil halt zuerst der Aufruf kommt, dass mit 1000 subtrahiert werden soll.

Probier's doch mal mit:
unsigned long volumen = ((1000* entfernung) / 794) -1000;

Das sollte dann auch hinhauen, zumindestens kamen bei mir dann die exakt gleichen Werte, wie bei deiner ausführlichen Variante raus. :-)

Grund hierbei liegt einfach dabei, wie der Compiler Ausdrücke auswertet und diese dann in Maschinen-Code umwandelt. Ich meine du hast ja bei deiner ausführlichen Variante auch selbst zuerst durch 794 geteilt und dann erst 1000 davon abgezogen. Du musst einfach die richtige Rechenreihenfolge beibehalten, dann klappt's auch.

Außerdem sei noch gesagt, soweit ich mich richtig erinner', speichern unsigned long-Werte keine negativen Werte, die gehen also nur von 0 bis schlagmichtot.

Bei deiner nicht ganz korrekt zusammengefassten Berechnung würde ja eine negative Zahl rauskommen, deswegen schmeisst er dir dann auch einen Fehler.

Allgemein wunder ich mich warum du da nicht auch mit int arbeitest, das würde mich schon interessieren.

Viel Spaß dir noch :-)


Guenfrgen1231 
Fragesteller
 31.07.2023, 11:03

Danke erst mal.
Ich habe es schon mit int probiert. Aber da meine Entfernung um die 500 ist wäre das Ergebnis zwischenzeitlich größer als die max. Speichergröße des int glb sind 2 bytes also 16 bits und damit kann man ca. von 0-65.000 speichern. Da aber 1000*500 größer als 65.000 ist dachte ich mir, dass int nicht geht.

Kurze Frage noch: Ist es egal wenn das Zwischenergebnis größer als 65.000 ist oder muss nur das Endergebnis zwischen 0 und 65.000 liegen.
Danke :)

0
Guenfrgen1231 
Fragesteller
 31.07.2023, 11:21

Ich verstehe aber das auch nicht:

unsigned long volumen = 1000* 756/ 794;

Ich gebe genau diese Rechnung ein 1000*756/794
Aber als Lösung bekomme ich -38

Das ist mir irgendwie ein Rätsel

Ich habe herrausgefunden, dass da ein Fehler ist:

unsigned long volumen = 1000* 756/ 794;
Nach dieser Zeile ist das volumen so groß: 4294967258 = 0xffffffda

Kannst du mir das bitte erklären wieso?

0

War Int auf Arduino C nicht default 4Byte, wie Long ( das lässt sich bei den meisten Compiler aber einstellen)?

Wenn int nur 16Bit wie short hat, dann ist klar, dass er sich " verrechnet".

Wie du schon selbst bemerkt hast, ist der Wertebereich unsigned int 0 bis 65535 bzw. signed -32.768 bis 32.767.

Du hast einen klassischen Wertebereichüberlauf bei (Entfernung * 1000) modulo 65536 und dann noch signed... und unsigned durcheinander gemischt. MERKE: nur wenn du dir absolut sicher bist, dürfen signed und unsigned Variablen/Zahlen gleichzeitig benutzt werden.

Im zweiten Beispiel Code benutzt du ja Entfernung mit Typ Int und multiplizierst das mit Int 1000. Du hättest einen Cast gebraucht: (long) Entfernung oder einfach statt 1000 1000L nutzen können. Es ist immer wieder interessant was C Compiler so alles treiben, wenn Sie den Source Code transformieren, denn konstante Ganzahlen, die nicht offensichtlich als ein bestimmter Typ deklariert werden, sind implizit vom Typ int!

So zum mitrechnen mit Entfernung=756:

756*1000 % 65536 =35104, wenn unsigned

Mit signed gilt aber:

35104 ist größer 32.767, also muss man das zweier Komplement bilden, d.h. 35104 - 65536 = -30432, das dann durch die 794, ergibt -38. Und 1000 - (-38) sind eben 1038.

Noch mal zur Verdeutlichung: Die Bitwerte im 16 Bit Rechenergebnis sind fest, man muss aber diese Bits nach Vorzeichen oder nicht Vorzeichen, im Wertebereiche interpretieren.

So ich hoffe ich habe jetzt keine Flüchtigkeitsfehler gemacht, aber so kommt man auf dieses Ergebnis.

Mach dir nichts draus, meine Studenten in Grundlagen der Informatik machten auch solche Fehler.

Woher ich das weiß:Studium / Ausbildung – Dipl. Math., BOS, Elektronik/Elektriker, Lebenserfahrung

W00dp3ckr  31.07.2023, 17:53

Schön durchgezogen.

1

Nein, Rechner rechnen nicht komisch, sondern so wie es festgelegt ist.

Es wäre natürlich interessant, was Du überhaupt als Ergebnis erwartest und warum.

Ein grundlegendes Problem ist, daß Du eine Berechnung ausführst, die im Ergebnis negativ ist und dafür einen vorzeichenlosen Typ nimmst.

Die Reihenfolgen sind unterschiedlich, im ersten Fall:

Konversion des int zu unsigned, long daraufhin Berechnung, wobei im letzten Schritt ein vorzeichenloses Zwischenergebnis von einer Konstanten abgezogen wird, zu einem negativen Ergebnis führt und einfach dem unsigned zugewiesen wird. Das return konvertiert implizit zurück.

Im anderen Fall wird der vorzeichenbehaftete int ohne Konversion multipliziert, das Ergebnis dividiert und dann von 1000 (Konstante) abgezogen. Erst jetzt wird zur vorzeichenlosen Zahl konvertiert und mit dem return zurückkonvertiert.

Unterschiedliche Abfolgen können je nach Werten dann natürlich zu unterschiedlichen Ergebnissen führen.

Nun ggf. sagst du uns was für Werte du verwendet hast und was für Ergebnisse du bekommen hast?

Ich würde mal tippen, dass Entfernung * 1000 in dem Fall den Wertebereich des int von Entfernung überschritten hat, während der unsigned long von Volumen den Wert aufnehmen kann, das ist vermutlich der Grund, warum du einen long verwendest.

Die Klammern brauchst du auch nicht, da ohnehin erst Punkt vor Strich gerechnet wird. Natürlich kannst du auch direkt den Wert zurückgeben und brauchst die Variable Volumen nicht.

int Volumen(int Entfernung) {
    return 1000 - (unsigned long) Entfernung * 1000 / 794;
}

Wäre wohl die Alternative zu deinem eigentlichen Code.

Es wird also zuerst Entfernung in ein unsigned long umgewandelt. Dann mit 1000 multipliziert und dann durch 794 geteilt und dieser Wert von 1000 abgezogen.

Woher ich das weiß:Berufserfahrung – Softwareentwickler/Projektleiter seit 2012

Tommentator  31.07.2023, 17:48

Ich würde empfehlen dass es in (signed) long umgewandelt wird

0
apachy  31.07.2023, 18:25

Mal ergänzend, wo man deine Werte oben im Kommentar sieht. Dein int im Arduino hat 16 Bit, geht also nur bis 32767.

Du multiplizierst 756 mit 1.000 was 756.000 wäre. Aufgrund des Überlaufs wird daraus -38.

Anschließend rechnest du 1.000 - -38. Minus und Minus ist Plus also 1.000 + 38 = 1.038.

0