Integer Summing Blues, kurz + = kurzes Problem

72

Programm in C #:

short a, b;
a = 10;
b = 10;
a = a + b; // Error : Cannot implicitly convert type 'int' to 'short'.

// we can also write this code by using Arithmetic Assignment Operator as given below

a += b; // But this is running successfully, why?

Console.Write(a);
Mohammad Jahangeer Ansari
quelle
5
Der erste schlägt fehl, weil short + short = int wie in der Spezifikation definiert ist, genau wie byte + byte = int. Ich hätte jedoch erwartet, dass auch der zweite fehlschlägt, und freue mich darauf, die Argumentation hier zu sehen.
Øyvind Bråthen
Hast du einen Grund gefunden?
Mohammad Jahangeer Ansari
Dies wird unten von Eric Lippert so detailliert wie möglich beantwortet, daher bin ich mir nicht sicher, was Sie hier fragen. Kurz gesagt ist, weil a += bist das gleiche wiea = (short)(a+b)
Øyvind Bråthen
Mögliches Duplikat von Byte + Byte = int ... warum?
Mixxiphoid

Antworten:

77

Hier gibt es zwei Fragen. Das erste ist "Warum ist kurz plus kurzes Ergebnis in int?"

Angenommen, kurz plus kurz war kurz und sehen, was passiert:

short[] prices = { 10000, 15000, 11000 };
short average = (prices[0] + prices[1] + prices[2]) / 3;

Und der Durchschnitt liegt natürlich bei -9845, wenn diese Berechnung in kurzen Abständen durchgeführt wird. Die Summe ist größer als der größtmögliche Short, daher wird sie negativ und Sie teilen die negative Zahl.

In einer Welt, in der sich ganzzahlige Arithmetik umgibt, ist es viel sinnvoller, alle Berechnungen in int durchzuführen, einem Typ, der wahrscheinlich genug Reichweite hat, damit typische Berechnungen nicht überlaufen.

Die zweite Frage lautet:

  • kurz plus kurz ist int
  • Das Zuweisen von int zu short ist illegal
  • a + = b ist dasselbe wie a = a + b
  • daher sollte short + = short illegal sein
  • Warum ist das legal?

Die Frage hat eine falsche Prämisse; Die dritte Zeile oben ist falsch. Die C # -Spezifikation lautet in Abschnitt 7.17.2

Andernfalls, wenn der ausgewählte Operator ein vordefinierter Operator ist, wenn der Rückgabetyp des ausgewählten Operators explizit in den Typ x konvertierbar ist und wenn y implizit in den Typ x konvertierbar ist oder der Operator ein Schichtoperator ist, dann ist die Operation wird bewertet als x = (T) (x op y), wobei T der Typ von x ist, außer dass x nur einmal ausgewertet wird.

Der Compiler fügt die Besetzung in Ihrem Namen ein. Die richtige Begründung lautet:

  • kurz plus kurz ist int
  • Das Zuweisen von int zu short ist illegal
  • s1 + = s2 ist dasselbe wie s1 = (kurz) (s1 + s2)
  • Daher sollte dies legal sein

Wenn die Besetzung nicht für Sie eingefügt würde, wäre es unmöglich, die zusammengesetzte Zuweisung für viele Typen zu verwenden.

Eric Lippert
quelle
11
Ihre erste Probe ist nicht gut. Wenn Sie ersetzen shortmit intallem gut funktionieren, aber wenn wir größere Zahlen setzen (zu Summe macht größer als 2 ^ 31) wird es int Überlauf verursachen. Meine Frage ist warum short + short = int(und für byte) und int + int = int(nicht lange zum Beispiel).
Andrey
8
@LBushkin: Ist es wirklich "genauso wahrscheinlich"? Ich schreibe Compiler für meinen Lebensunterhalt und beschäftige mich jeden Tag mit dem Kompilieren von Programmen, die mehr als beispielsweise 2 ^ 15 Typen haben. Wenn ich die Anzahl der Typen in einer Reihe verschiedener Baugruppen addiere, kann ein Kurzschluss leicht überlaufen. Ich hatte noch nie ein Problem im Compiler-Bereich, bei dem ich einen Int vernünftigerweise überlaufen konnte. Dem Compiler würde der virtuelle Speicher ausgehen, lange bevor es ihm gelang, mit einem Programm zu rechnen, das zwei Milliarden Typen enthält. In den meisten Szenarien ist ein 32-Bit-Überlauf einfach nicht wahrscheinlich.
Eric Lippert
6
@Eric: int wird in vielen Rechenkontexten verwendet, von denen einige keine Beziehung zum Speicher- / Prozessraum haben. Mit zunehmender Leistung und Kapazität der Computerhardware werden diese Grenzen regelmäßig ziemlich bald überschritten. Eine bessere Möglichkeit, meinen Standpunkt auszudrücken, besteht darin, dass der Compiler, wenn er sich entscheidet, kleinere Typen bei der Durchführung von Arithmetik auf breitere Typen auszudehnen, um einen unerwarteten Überlauf zu vermeiden, dies mit allen Typen tun sollte, für die ein breiterer Typ verfügbar ist, der den Bereich und die Darstellungsgenauigkeit beibehält. Die Wahl, bei int anzuhalten (und nicht zu lange zu erweitern), ist etwas unerwartet.
LBushkin
5
@LBushkin: OK, würden Sie uns (1) sagen lassen, dass int + int lang ist? Dies ist gleichbedeutend damit, dass alle Berechnungen immer in langen Schritten durchgeführt werden . Sie möchten nicht, dass Personen in jede Ganzzahlberechnung Casts einfügen. (2) Hat C # je nach Architektur ein unterschiedliches Verhalten wie C? (3) automatisch auf einen größeren Typ erweitern, wie dies VBScript tut; Geben Sie die statische Eingabe in C # auf. (4) noch etwas? Jeder dieser Ansätze hat erhebliche Leistungs- oder Portabilitätskosten. Sind die Kosten es wert?
Eric Lippert
4
@Eric: Außerdem hatte ich den Eindruck, dass es zumindest in der x86-Architektur möglich ist, Verschiebungen, Ladevorgänge und Arithmetik nur für nicht erweiterte 16-Bit-Register durchzuführen. Sie können auch auf die niedrigen und hohen 8-Bit-Unterregister zugreifen (z. B. AL und AH in AX). Offensichtlich kann das Verhalten auf Nicht-x86-Architekturen wesentlich anders sein.
LBushkin
13

Nun, der +=Operator sagt, dass Sie den Wert von amit einem Kurzschluss erhöhen werden , während er =sagt , dass Sie den Wert mit dem Ergebnis einer Operation überschreiben werden. Die Operation a + bergibt ein int, ohne zu wissen, dass dies anders möglich ist, und Sie versuchen, dieses int einem Kurzschluss zuzuweisen.

David Hedlund
quelle
1
Was ist dann der Unterschied zwischen Wertsteigerung und Wertschöpfung?
Mohammad Jahangeer Ansari
Das habe ich wirklich nicht erwartet. Ich dachte +=nur eine Abkürzung!
Camilo Martin
@jak Ich verstehe es, wenn der =Operator einen neuen Wert zuweist (in diesem Fall vom falschen Typ), aber der +=verwendet eine andere Implementierung (dh er fügt hinzu, wie wir es erwarten).
Camilo Martin
Ich habe auch immer gedacht, dass a + = b nur eine Abkürzung für a = a + b ist, deshalb würde ich mir hier wirklich eine Bestätigung wünschen, um vollständig überzeugt zu sein.
Øyvind Bråthen
@ Øyvind Bråthen es scheint so zu sein: a+=b= es a=a+bsei denn, der Operator + = ist überladen, und es scheint, dass es für den Typ shortist und es kein +für gibt short, shortA + shortBerweitert und wird (int)shortA + (int)shortB.
Camilo Martin
9

Sie müssen verwenden:

a = (short)(a + b);

Ich stelle mir vor, dass der Unterschied zwischen dem Verhalten der Zuweisung und der Hinzufügungszuweisung etwas damit zu tun hat (von msdn).

x+=y
is equivalent to
x = x + y
except that x is only evaluated once. The meaning of the + operator is
dependent on the types of x and y (addition for numeric operands, 
concatenation for string operands, and so forth).

Es ist jedoch etwas vage, so dass mabye jemand mit einem tieferen Verständnis kommentieren kann.

UpTheCreek
quelle
1
@jak: hast du es mit beiden Klammern versucht? Der Code in Ihrem Kommentar ist anders.
UpTheCreek
2
Wo steht das auf MSDN? Das ist falsch, wenn das + ein vordefinierter Operator ist , was in diesem Fall eindeutig der Fall ist. In diesem Fall entspricht es wie angegeben x = (T) (x + y). Wenn Sie mir den Link zur Seite senden können, werde ich die Dokumentationsmanager darauf aufmerksam machen.
Eric Lippert
2
@ Eric: Sicher, es ist auf dieser Seite: msdn.microsoft.com/en-us/library/sa7629ew.aspx
UpTheCreek
7

Dies geschieht, weil int der kleinste vorzeichenbehaftete Typ ist, für den +definiert ist. Alles, was kleiner ist, wird zuerst zu int befördert. Der +=Operator wird in Bezug auf definiert +, jedoch mit einer Sonderfallregel für die Behandlung von Ergebnissen, die nicht zum Ziel passen.

Marcelo Cantos
quelle
3
Diese Antwort ist falsch. + = ist nicht für jeden numerischen Typ definiert. Siehe Abschnitt 7.17.2 der C # -Spezifikation.
Eric Lippert
1

Dies liegt daran, dass + = als überladene Funktion implementiert ist (eine davon ist eine kurze Funktion, und der Compiler wählt die spezifischste Überladung aus). Für den Ausdruck (a + b) erweitert der Compiler das Ergebnis vor dem Zuweisen standardmäßig auf ein int.

Stefan Mai
quelle
1
Überladener Operator kann nur mit benutzerdefinierten Operatoren wie Klasse, Struktur implementiert werden. Es handelt sich jedoch um einen + eingebauten arithmetischen Operator. Der Operator + = kann nicht direkt überladen werden, aber benutzerdefinierte Typen können den Operator + überladen. Weitere
Mohammad Jahangeer Ansari
1
Jak ist richtig. Der Operator + = ist keine überladene Funktion in C #.
Eric Lippert