Betrachten Sie diesen C # -Code:
byte x = 1;
byte y = 2;
byte z = x + y; // ERROR: Cannot implicitly convert type 'int' to 'byte'
Das Ergebnis einer für byte
(oder short
) Typen durchgeführten Mathematik wird implizit auf eine Ganzzahl zurückgesetzt. Die Lösung besteht darin, das Ergebnis explizit in ein Byte umzuwandeln:
byte z = (byte)(x + y); // this works
Was ich mich frage ist warum? Ist es architektonisch? Philosophisch?
Wir haben:
int
+int
=int
long
+long
=long
float
+float
=float
double
+double
=double
Also warum nicht:
byte
+byte
=byte
short
+short
=short
?
Ein bisschen Hintergrund: Ich führe eine lange Liste von Berechnungen für "kleine Zahlen" (dh <8) durch und speichere die Zwischenergebnisse in einem großen Array. Die Verwendung eines Byte-Arrays (anstelle eines int-Arrays) ist schneller (aufgrund von Cache-Treffern). Aber die umfangreichen Byte-Casts, die über den Code verteilt sind, machen ihn umso unlesbarer.
c#
type-conversion
Robert Cartaino
quelle
quelle
byte1 | byte2
behandelt sie überhaupt nicht als Zahlen. Dies behandelt sie genau als Muster von Bits. Ich verstehe Ihren Standpunkt, aber es kommt einfach so vor, dass ich jedes Mal, wenn ich in C # eine Arithmetik für Bytes durchgeführt habe, diese tatsächlich als Bits und nicht als Zahlen behandelte, und dieses Verhalten ist immer im Weg.Antworten:
Die dritte Zeile Ihres Code-Snippets:
bedeutet eigentlich
Es gibt also keine + Operation für Bytes, Bytes werden zuerst in Ganzzahlen umgewandelt und das Ergebnis der Addition von zwei Ganzzahlen ist eine (32-Bit) Ganzzahl.
quelle
int
erfolgt: stackoverflow.com/a/43578929/4561887In Bezug auf "warum es überhaupt passiert" liegt es daran, dass es keine durch C # definierten Operatoren für die Arithmetik mit Byte, Sbyte, Kurz oder Ushort gibt, wie andere gesagt haben. In dieser Antwort geht es darum, warum diese Operatoren nicht definiert sind.
Ich glaube, es ist im Grunde genommen der Leistung zuliebe. Prozessoren haben native Operationen, um sehr schnell mit 32 Bit zu rechnen. Das Zurücksetzen der Konvertierung vom Ergebnis in ein Byte könnte automatisch erfolgen, würde jedoch zu Leistungseinbußen führen, wenn Sie dieses Verhalten nicht wirklich möchten.
Ich denke, dies wird in einem der kommentierten C # -Standards erwähnt. Suchen ...
EDIT: Ärgerlicherweise habe ich jetzt die kommentierte ECMA C # 2-Spezifikation, die kommentierte MS C # 3-Spezifikation und die Annotations-CLI-Spezifikation durchgesehen, und keiner von ihnen erwähnt dies, soweit ich sehen kann. Ich bin sicher, ich habe den oben angegebenen Grund gesehen, aber ich bin überwältigt, wenn ich weiß, wo. Entschuldigung, Referenzfans :(
quelle
Ich dachte, ich hätte das schon einmal gesehen. Aus diesem Artikel, The Old New Thing :
EDIT : Raymond verteidigt im Wesentlichen den Ansatz, den C und C ++ ursprünglich gewählt haben. In den Kommentaren verteidigt er die Tatsache, dass C # aus Gründen der Sprachabwärtskompatibilität denselben Ansatz verfolgt.
quelle
C #
ECMA-334 besagt, dass die Addition nur für int + int, uint + uint, long + long und ulong + ulong als legal definiert ist (ECMA-334 14.7.4). Als solche sind dies die Kandidatenoperationen, die in Bezug auf 14.4.2 zu berücksichtigen sind. Da es implizite Casts von Byte zu int, uint, long und ulong gibt, sind alle Additionsfunktionsmitglieder anwendbare Funktionsmitglieder unter 14.4.2.1. Wir müssen die beste implizite Besetzung durch die Regeln in 14.4.2.3 finden:
Das Casting (C1) nach int (T1) ist besser als das Casting (C2) nach uint (T2) oder ulong (T2), weil:
Das Umwandeln von (C1) nach int (T1) ist besser als das Umwandeln von (C2) nach long (T2), da implizit von int nach long umgewandelt wird:
Daher wird die Funktion int + int verwendet, die ein int zurückgibt.
Das ist alles ein sehr langer Weg, um zu sagen, dass es sehr tief in der C # -Spezifikation vergraben ist.
CLI
Die CLI arbeitet nur mit 6 Typen (int32, native int, int64, F, O und &). (ECMA-335 Partition 3, Abschnitt 1.5)
Byte (int8) gehört nicht zu diesen Typen und wird vor dem Hinzufügen automatisch zu einem int32 gezwungen. (ECMA-335 Partition 3, Abschnitt 1.6)
quelle
byte3 = byte1 And byte2
ohne Umwandlung hilfreich zu, löst jedoch nicht hilfreich eine Laufzeitausnahme aus, wennint1 = byte1 + byte2
ein Wert über 255 ausgegeben wird. Ich weiß nicht, ob Sprachenbyte3 = byte1+byte2
eine Ausnahme zulassen und auslösen würden, wenn diese 255 überschreitet, aber keine Ausnahme, wennint1 = byte1+byte2
Ausbeuten ein Wert im Bereich von 256-510.Die Antworten, die auf eine Ineffizienz beim Hinzufügen von Bytes und beim Zurückschneiden des Ergebnisses auf ein Byte hinweisen, sind falsch. x86-Prozessoren verfügen über Anweisungen, die speziell für den Ganzzahlbetrieb mit 8-Bit-Mengen entwickelt wurden.
Tatsächlich ist bei x86 / 64-Prozessoren die Ausführung von 32-Bit- oder 16-Bit-Operationen aufgrund des zu decodierenden Operandenpräfixbytes weniger effizient als 64-Bit- oder 8-Bit-Operationen. Auf 32-Bit-Computern ist das Ausführen von 16-Bit-Operationen mit derselben Strafe verbunden, es gibt jedoch immer noch dedizierte Opcodes für 8-Bit-Operationen.
Viele RISC-Architekturen verfügen über ähnliche native wort- / byteeffiziente Anweisungen. Diejenigen, die im Allgemeinen keinen Wert für das Speichern und Konvertieren in einen signierten Wert mit einer gewissen Bitlänge haben.
Mit anderen Worten, diese Entscheidung muss auf der Wahrnehmung des Byte-Typs beruhen, nicht auf zugrunde liegenden Ineffizienzen der Hardware.
quelle
byte
(undchar
) erklären , aber nicht, fürshort
das semantisch eindeutig eine Zahl ist.Ich erinnere mich, dass ich einmal etwas von Jon Skeet gelesen habe (ich kann es jetzt nicht finden, ich werde weiter suchen), wie Byte den Operator + nicht wirklich überlastet. Wenn Sie zwei Bytes wie in Ihrem Beispiel hinzufügen, wird jedes Byte implizit in ein int konvertiert. Das Ergebnis davon ist offensichtlich ein int. Nun, WARUM dies so entworfen wurde, ich werde warten, bis Jon Skeet selbst etwas veröffentlicht :)
EDIT: Gefunden! Tolle Infos zu diesem Thema hier .
quelle
Dies liegt am Überlauf und trägt.
Wenn Sie zwei 8-Bit-Zahlen hinzufügen, können diese in das 9. Bit überlaufen.
Beispiel:
Ich weiß es nicht genau, aber ich nehme an, dass
ints
,longs
unddoubles
ist mehr Raum gegeben , weil sie ziemlich groß sind , wie es ist. Außerdem handelt es sich um Vielfache von 4, die für Computer effizienter zu handhaben sind, da die Breite des internen Datenbusses 4 Byte oder 32 Bit (64 Bit werden jetzt immer häufiger) breit ist. Byte und Short sind etwas ineffizienter, können aber Platz sparen.quelle
Aus der C # -Sprachenspezifikation 1.6.7.5 7.2.6.2 Binäre numerische Heraufstufungen werden beide Operanden in int konvertiert, wenn sie nicht in mehrere andere Kategorien eingeteilt werden können. Ich vermute, sie haben den Operator + nicht überladen, um Byte als Parameter zu verwenden, sondern möchten, dass er sich normal verhält, sodass sie nur den Datentyp int verwenden.
C # Sprachspez
quelle
Mein Verdacht ist, dass C # tatsächlich das
operator+
definierte on aufruftint
(was ein zurückgibt, esint
sei denn, Sie befinden sich in einemchecked
Block) und implizit beidebytes
/shorts
to umwandeltints
. Deshalb erscheint das Verhalten inkonsistent.quelle
Dies war wahrscheinlich eine praktische Entscheidung der Sprachdesigner. Immerhin ist ein int ein Int32, eine 32-Bit-Ganzzahl mit Vorzeichen. Wenn Sie eine Ganzzahloperation für einen Typ ausführen, der kleiner als int ist, wird sie ohnehin von den meisten 32-Bit-CPUs in ein 32-Bit-Int mit Vorzeichen konvertiert. Dies, zusammen mit der Wahrscheinlichkeit, dass kleine ganze Zahlen überlaufen, besiegelte wahrscheinlich den Deal. Dies erspart Ihnen die mühsame ständige Überprüfung auf Über- / Unterlauf. Wenn das Endergebnis eines Ausdrucks für Bytes im Bereich liegt, obwohl er in einem Zwischenstadium außerhalb des Bereichs liegt, erhalten Sie ein korrektes Ergebnis Ergebnis.
Ein weiterer Gedanke: Der Über- / Unterlauf dieser Typen müsste simuliert werden, da er auf den wahrscheinlichsten Ziel-CPUs nicht auf natürliche Weise auftreten würde. Warum die Mühe?
quelle
Dies ist größtenteils meine Antwort zu diesem Thema, die hier zunächst einer ähnlichen Frage gestellt wurde .
Alle Operationen mit ganzzahligen Integralzahlen kleiner als Int32 werden vor der Berechnung standardmäßig auf 32 Bit aufgerundet. Der Grund, warum das Ergebnis Int32 ist, besteht darin, es einfach so zu belassen, wie es nach der Berechnung ist. Wenn Sie die arithmetischen MSIL-Opcodes überprüfen, sind Int32 und Int64 die einzigen integralen numerischen Typen, mit denen sie arbeiten. Es ist "von Natur aus".
Wenn Sie das Ergebnis wieder im Int16-Format wünschen, ist es unerheblich, ob Sie die Umwandlung in Code durchführen oder der Compiler (hypotetisch) die Konvertierung "unter der Haube" ausgibt.
Zum Beispiel, um Int16-Arithmetik auszuführen:
Die zwei Zahlen würden auf 32 Bit erweitert, hinzugefügt und dann auf 16 Bit zurückgeschnitten, wie es MS beabsichtigt hatte.
Der Vorteil der Verwendung von Short (oder Byte) besteht hauptsächlich in der Speicherung in Fällen, in denen Sie über große Datenmengen verfügen (grafische Daten, Streaming usw.).
quelle
Die Addition ist für Bytes nicht definiert. Also werden sie für die Hinzufügung zu int gegossen. Dies gilt für die meisten mathematischen Operationen und Bytes. (Beachten Sie, dass dies früher in älteren Sprachen der Fall war. Ich gehe davon aus, dass dies heute der Fall ist.)
quelle
Ich denke, es ist eine Entwurfsentscheidung darüber, welche Operation häufiger war ... Wenn Byte + Byte = Byte, werden möglicherweise viel mehr Menschen gestört, wenn sie in int umwandeln müssen, wenn ein int als Ergebnis erforderlich ist.
quelle
Aus .NET Framework-Code:
Vereinfachen Sie mit .NET 3.5 und höher:
Jetzt können Sie tun:
quelle
Ich habe die Leistung zwischen Byte und Int getestet.
Mit int-Werten:
Mit Bytewerten:
Hier das Ergebnis:
Byte: 3,57s 157mo, 3,71s 171mo, 3,74s 168mo mit CPU ~ = 30%
int: 4,05s 298mo, 3,92s 278mo, 4,28 294mo mit CPU ~ = 27%
Fazit:
Byte verbraucht mehr CPU, aber es kostet les Speicher und es ist schneller (möglicherweise, weil weniger Byte zuzuweisen ist)
quelle
Zusätzlich zu all den anderen tollen Kommentaren dachte ich, ich würde einen kleinen Leckerbissen hinzufügen. Viele Kommentare haben sich gefragt, warum int, long und so ziemlich jeder andere numerische Typ dieser Regel nicht ebenfalls folgt. Geben Sie als Antwort auf arithmatisch einen "größeren" Typ zurück.
Viele Antworten haben mit der Leistung zu tun (32 Bit sind schneller als 8 Bit). In Wirklichkeit ist eine 8-Bit-Nummer immer noch eine 32-Bit-Nummer für eine 32-Bit-CPU. Selbst wenn Sie zwei Bytes hinzufügen, beträgt der Datenblock, mit dem die CPU arbeitet, unabhängig davon 32 Bit. Das Hinzufügen von Ints ist also nicht erforderlich sei "schneller" als das Hinzufügen von zwei Bytes ... es ist alles das Gleiche zur CPU. JETZT ist das Hinzufügen von zwei Ints schneller als das Hinzufügen von zwei Longs auf einem 32-Bit-Prozessor, da das Hinzufügen von zwei Longs mehr Mikroops erfordert, da Sie mit Zahlen arbeiten, die breiter als das Wort des Prozessors sind.
Ich denke, der grundlegende Grund dafür, dass Byte-Arithmetik zu Ints führt, ist ziemlich klar und direkt: 8 Bits gehen einfach nicht sehr weit! : D Mit 8 Bit haben Sie einen vorzeichenlosen Bereich von 0-255. Das ist nicht viel Platz zum Arbeiten ... die Wahrscheinlichkeit, dass Sie auf Byte-Einschränkungen stoßen, ist SEHR hoch, wenn Sie sie in der Arithmetik verwenden. Die Wahrscheinlichkeit, dass Ihnen bei der Arbeit mit Ints, Longs, Doubles usw. die Bits ausgehen, ist jedoch erheblich geringer ... so gering, dass wir sehr selten auf den Bedarf an mehr stoßen.
Die automatische Konvertierung von Byte nach Int ist logisch, da der Maßstab eines Bytes so klein ist. Die automatische Konvertierung von int nach long, float nach double usw. ist nicht logisch, da diese Zahlen eine signifikante Skalierung haben.
quelle
byte - byte
zurückkehrtint
oder warum sie nicht zushort
...byte + byte
zurückgegeben wirdint
, weil 255 + alles größer ist, als ein Byte enthalten kann, ist es unter dem Gesichtspunkt der Konsistenz des Rückgabetyps nicht sinnvoll, dass ein Byte minus ein anderes Byte etwas anderes als ein int zurückgibt.byte
hätte , würde die Subtraktion abyte
und die Byte-Addition a zurückgebenshort
(byte
+byte
passt immer in ashort
). Wenn es um Konsistenzshort
ginge, wie Sie sagen, würde dies für beide Operationen eher ausreichen alsint
. Natürlich gibt es eine Mischung aus Gründen, von denen nicht alle unbedingt gut durchdacht sind. Oder der unten angegebene Leistungsgrund ist möglicherweise genauer.