Signed long mit -1 multiplizeren

#5556739
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
Beitrag #5556762 wurde von einem Moderator gelöscht.
#5556847
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.
Gast #5556857
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. :(
#5556865
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.
#5557398
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.
Gast #5557493
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.
#5557522
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.
Gast #5557616
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.
Gast #5558322
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)
#5558506
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
Gast #5558510
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.
#5558536
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.
#5558541
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren