Forum: Compiler & IDEs Signed long mit -1 multiplizeren


von Thomas S. (thomas_s72)


Lesenswert?

Hallo,
ich habe da ein komisches Problem mit der Multiplikation mit -1.
AVR Studio 7, Atmega32.

FACTOR = 1000000UL;
signed long result = 176000000L;
signed int tmp = result/FACTOR;
printf("Result: %d\n", tmp);

if (IsNegative)  result *= (-1);

tmp = result/FACTOR;
printf("Result: %d\n", tmp);

Ich berechne da ein signed Long, das hat dann den Wert 176Mio. Wenn ich 
das herunter teile und über den COM Port ausgebe bekomme ich 176, passt.
Wenn ich aber mit (-1) multipliziere weil mein Flag das sagt, dann 
bekomme ich 4118 ausgegeben, nicht -176.
Anders gefragt: Wie mache ich aus einer positiven Long Zahl eine 
negative? So wie oben geht es nicht.
Das Problem ist ganz eindeutig die Multiplikation, vorher passt es, 
nachher nicht.

Besten Dank, Gruß Thomas

von Wolfgang (Gast)


Lesenswert?

Thomas S. schrieb:
> Das Problem ist ganz eindeutig die Multiplikation, vorher passt es,
> nachher nicht.

Zweierkomplement direkt rechnen, i.e. invertieren und +1

von (prx) A. K. (prx)


Lesenswert?

Thomas S. schrieb:
> tmp = result/FACTOR;

Das ist eine vorzeichenlose Division, da zwar der Zähler "signed long" 
ist, der Nenner aber "unsigned long".

: Bearbeitet durch User
von Theor (Gast)


Lesenswert?

1 Million durch 176 Millionen ergibt niemals 167.

Der Code passt nicht zu Deinem Text. Bitte überarbeite das nochmal. Und 
poste hier am besten kompilierbaren Code.

von (prx) A. K. (prx)


Lesenswert?

Wolfgang schrieb:
> Zweierkomplement direkt rechnen, i.e. invertieren und +1

Wozu soll das denn gut sein?

Allerdings könnte man auf die Idee kommen, einfach
   result = -result;
hinzuschreiben.

von Theor (Gast)


Lesenswert?

Au weia. Ich habe mich verguckt. Die Division ist durchaus richtig. 
Sorry.

Beitrag #5556762 wurde vom Autor gelöscht.
von A. S. (Gast)


Lesenswert?


von Ingolf (Gast)


Lesenswert?

Weiß jemand, warum C in so einem Fall zu unsigned konvertiert, also was 
ist die logische Begründung (Praxisbezug)? Auf den ersten Blick 
erscheint mir das unsinnig und irreführend.

von (prx) A. K. (prx)


Lesenswert?

Ingolf schrieb:
> Weiß jemand, warum C in so einem Fall zu unsigned konvertiert, also was
> ist die logische Begründung (Praxisbezug)? Auf den ersten Blick
> erscheint mir das unsinnig und irreführend.

Was wäre die Alternative und warum wäre sie sinniger?

Tatsächlich steckt dieser Aspekt bereits von Anbeginn in C. Während 
die Situation bei
   int16 / uint8
erst mit ANSI-C entschieden wurde. Dennis Ritchie kannst du jedenfalls 
nicht mehr fragen.

Beitrag #5556850 wurde von einem Moderator gelöscht.
Beitrag #5556853 wurde von einem Moderator gelöscht.
von Ingolf (Gast)


Lesenswert?

Die Alternative wäre, dass eben signed quasi dominant ist, also nach 
signed promoted wird, wenn einer der Operanden signed ist. Weil das 
Vorzeichen eine Information ist (die offensichtlich wichtig ist, sonst 
wäre die Variable ja nicht signed). Diese Information geht ungefragt und 
per Default verloren.

So, wie es jetzt ist, das ist für mich ähnlich logisch, als würde C von 
long nach int "promoten", wenn man z.B. long + int rechnet.
Ein Designfehler. Kann natürlich sein, dass ich mich täusche und es eine 
logische Erklärung gibt. Aber auf solche Späße stößt man in C leider 
wirklich oft, was wirklich Schade ist. :(

von (prx) A. K. (prx)


Lesenswert?

Vorzeichenlose Rechnung ist in C als Modulo-Arithmetik definiert, 
während das Verhalten vorzeichenbehafteter Arithmetik bei Überlauf 
undefiniert ist. Die Dominanz des Vorzeichens würde damit etwas 
kollidieren.

Man könnte Rechnungen verschiedener Typen auch ganz verbieten, mit 
Pflicht zu expliziter Konvertierung in homogene Typen. Das wäre 
eindeutig. Dann gäbe
   result / FACTOR
eine Fehlermeldung, und nötig wäre sowas wie
   result / long(FACTOR)
Aber das wäre dann nicht mehr C.

: Bearbeitet durch User
von Ingolf (Gast)


Lesenswert?

Gibt es wenigstens Warnungen (-W???), welche man einschalten könnte und 
die auf solche Fälle hinweisen? Das wäre extrem hilfreich.

von Thomas S. (thomas_s72)


Lesenswert?

Hallo nochmal und vielen Dank für die zahlreichen Antworten.

Die Division war der Knackpunkt, signed geteilt durch unsigned.
Ich hab den FACTOR nun auch signed gemacht und siehe da es klappt!
Case gelöst, schönes Wochenende an alle und vielen Dank!

Gruß
Thomas

von Rolf M. (rmagnus)


Lesenswert?

A. K. schrieb im Beitrag #5556853:
> Oder überhaupt nicht zulassen?

Das wäre an sich gar nicht schlecht. Dann muss man sich vor so einer 
Operation nämlich immer selber Gedanken darüber machen, welcher Operand 
jetzt in den Typ des anderen konvertiert werden muss. Das Mischen von 
signed und unsigned bei Rechenoperationen ist sowieso selten eine gute 
Idee, wie man ja hier auch sieht.

Ingolf schrieb:
> Die Alternative wäre, dass eben signed quasi dominant ist, also nach
> signed promoted wird, wenn einer der Operanden signed ist. Weil das
> Vorzeichen eine Information ist (die offensichtlich wichtig ist, sonst
> wäre die Variable ja nicht signed). Diese Information geht ungefragt und
> per Default verloren.

Und bei unsigned ist es eben die Information, dass der Wert in der 
oberen Hälfte des Wertebereichs liegt. Ist die denn vollkommen unwichtig 
und darf deshalb einfach verloren gehen?
Beispiel: (für int = 16bit wie auf dem AVR)

signed int i = 5;
unsinged int j = 10000;

long result = i * j;

Was würdest du als Ergebnis erwarten? Nach den aktuellen Regeln steht in 
result nachher 50000 drin. Nach deinem Wunsch müsste die Rechnung 
vorzeichenbehaftet durchgeführt werden, wodurch das Ergebnis nicht mehr 
in den Wertebereich passt, was, wie schon erwähnt wurde, undefiniertes 
Verhalten bedeutet. Auf den meisten Systemen würde es vermutlich zu 
einem Wrap-Around kommen und das Ergebnis wäre -15536.

> So, wie es jetzt ist, das ist für mich ähnlich logisch, als würde C von
> long nach int "promoten", wenn man z.B. long + int rechnet.

Das ist was anderes, da int je nach Plattform weniger Informationen 
enthalten kann als long. Bei signed- und unsigned-Variante des selben 
Typs ist das aber anders.

Ingolf schrieb:
> Gibt es wenigstens Warnungen (-W???), welche man einschalten könnte und
> die auf solche Fälle hinweisen? Das wäre extrem hilfreich.

Ja.

von Ingolf (Gast)


Lesenswert?

Ich habe es gerade mal mit Delphi ausprobiert: Entweder rechnet dieses 
immer in einem sehr breiten Datentyp oder es richtet sich nach dem 
Ergebnistyp. Im Endeffekt heißt das, dass die Vorzeicheninformation 
(selbst bei gemischten Typen) dort nicht verloren geht.
Das Ergebnis deines Beispiels wäre dort korrekterweise 50000. Tausche 
ich 5 gegen -5, ist das Ergebnis weiterhin korrekt (-50000), 
vorausgesetzt, der Ergebnistyp ist signed und groß genug (den richtigen 
Typ für das Ergebnis muss der Programmierer natürlich schon selbst 
wählen).
Ich finde das so jedenfalls logischer und nachvollziehbarer.

von (prx) A. K. (prx)


Lesenswert?

Ausserhalb von Insellösungen aus einer Quelle oder für genau einen 
Compiler ist die formelle Sprachdefinition wichtiger als das, was man 
als "logisch" empfindet. Der Ansatz "mal ausprobieren was dabei raus 
kommt" ist dann nicht sehr hilfreich.

C hatte an ähnlicher Stelle einmal einen Bruch mit bestehendem 
Verhalten. Anfangs war in C offen, ob sich bei Operanden 
unterschiedlicher Grösse die Vorzeicheneigenschaft durchsetzt, oder der 
Wertebereich. Also ob bei beispielsweise
   int32 + ushort16
hinten int32 oder unsigned32 rauskommt. Bestehende Compiler waren 
uneinheitlich, mit Tendenz zur Vorzeicheneigenschaft. ANSI C definierte 
dann aber über den Wertebereich, also int32.

von mh (Gast)


Lesenswert?

Rolf M. schrieb:
> A. K. schrieb:
>> Oder überhaupt nicht zulassen?
>
> Das wäre an sich gar nicht schlecht. Dann muss man sich vor so einer
> Operation nämlich immer selber Gedanken darüber machen, welcher Operand
> jetzt in den Typ des anderen konvertiert werden muss. Das Mischen von
> signed und unsigned bei Rechenoperationen ist sowieso selten eine gute
> Idee, wie man ja hier auch sieht.

Der erste Schritt in die richtige Richtung ist -Wsign-conversion, damit 
der Compiler bei impliziten Vorzeichenkonvertierungen mit 
Informationsverlust (signed -> unsigned) warnt.

von c-hater (Gast)


Lesenswert?

A. K. schrieb:

> Vorzeichenlose Rechnung ist in C als Modulo-Arithmetik definiert,
> während das Verhalten vorzeichenbehafteter Arithmetik bei Überlauf
> undefiniert ist. Die Dominanz des Vorzeichens würde damit etwas
> kollidieren.

Naja, letztlich sind das auch wieder nur ein Haufen nutzloser Worte, die 
den Sachverhalt "C ist Scheisse" verschwurbelt umschreiben.

Dein Ansatz ist: C definiert das halt so und deshalb muss es gut sein. 
Und ich sage: nein, das ist Scheisse, schon die Definition taugt nix.

So sieht's aus. Und die Praxis gibt mir Recht. Wäre es anders, gäbe es 
nämlich z.B. diesen Thread erst garnicht. ;o)

von Ingolf (Gast)


Lesenswert?

Warum hat man damals eigentlich das Überlaufverhalten bei signed nicht 
definiert?

von Roland F. (rhf)


Lesenswert?

Hallo,

c-hater schrieb:
> Dein Ansatz ist: C definiert das halt so und deshalb muss es gut sein.

Nein, das das sein Ansatz ist unterstellst du ihm, für mich ist sein 
Ansatz eher : "C definiert das halt so".

> Und die Praxis gibt mir Recht.

Finde ich nicht, denn selbst so ein "Bastelprogrammierer" wie ich hat 
sofort erkannt das das mit den unterschiedlich definierten Datentypen zu 
tun haben muss.

Und vielleicht würde es manchen Fragestellern hier mal gut tun sich 
erstmal ein paar Gedanken über Probleme zu machen, bevor sie hier ihre 
Fragen stellen, dann
> ... gäbe es nämlich z.B. diesen Thread erst garnicht. ;o)

rhf

von A. S. (Gast)


Lesenswert?

Ingolf schrieb:
> Warum hat man damals eigentlich das Überlaufverhalten bei signed nicht
> definiert?

Weil eine sinnvolle Definition je nach Implementierung anders aussehen 
würde oder zusätzlichen Checker-code benötigt. und

Definieren heißt konkret: festlegen, was bei 127+1 bei einem signed char 
(@8Bit) rauskommen soll.

von (prx) A. K. (prx)


Lesenswert?

Ingolf schrieb:
> Warum hat man damals eigentlich das Überlaufverhalten bei signed nicht
> definiert?

Weil man auch nicht definiert hat, dass eine Rechnung mit Vorzeichen im 
Zweierkomplement durchzuführen ist. Mindestens bis in die 80er waren 
noch Maschinen im Einsatz, die im Einerkomplement rechneten. Daher auch 
der vom Standard geforderte Wertebereich von -32767..+32767 für int. 
Also ohne -32768.

Exkurs in andere Gefilde: Die erste Implementierung von Pascal entstand 
auf einer Maschine, die im Einerkomplement rechnete. Die offiziell 
maximal darstellbare Integer war zwar 2**48-1, aber das war nur das 
Limit von Multiplikation und Division. Ansonsten wurden 60 Bits 
gerechnet und gespeichert. Weshalb
   if abs(i) > maxint then write (' too big');
auf dieser Maschine ganz offiziell Sinn ergab. Allein schon, weil die 
Ausgabe von Integers ausserhalb des 49-Bit Bereichs scheiterte.

Zurück zu C: Versuch mal, da ein sinnvolles Überlaufverhalten zu 
definieren, ohne massiv an Effizienz zu verlieren. Da ist schon das 
definierte Verhalten vorzeichenloser Typen eine Herausfordeung.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Roland F. schrieb:
> c-hater schrieb:
>> Dein Ansatz ist: C definiert das halt so und deshalb muss es gut sein.
>
> Nein, das das sein Ansatz ist unterstellst du ihm, für mich ist sein
> Ansatz eher : "C definiert das halt so".

Korrekt. Er mag C hassen. Ich jedoch kenne und verwende C, ohne es zu 
lieben oder auch nur wirklich gut zu finden. Ist mit Deutsch nicht viel 
anders.

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
Noch kein Account? Hier anmelden.