Warum unterstützt Java keine vorzeichenlosen Ints?

374

Warum unterstützt Java keine Ganzzahlen ohne Vorzeichen?

Es scheint mir eine seltsame Auslassung zu sein, da sie es einem ermöglichen, Code zu schreiben, der bei unerwartet großen Eingaben weniger wahrscheinlich zu Überläufen führt.

Darüber hinaus kann die Verwendung von Ganzzahlen ohne Vorzeichen eine Form der Selbstdokumentation sein, da sie darauf hinweisen, dass der Wert, den das Int ohne Vorzeichen enthalten sollte, niemals negativ sein darf.

In einigen Fällen können vorzeichenlose Ganzzahlen für bestimmte Operationen, z. B. die Division, effizienter sein.

Was ist der Nachteil, wenn man diese einbezieht?

Dsimcha
quelle
137
Ich weiß es nicht, aber es nervt mich zum Teufel; Zum Beispiel ist es viel schwieriger, Netzwerkcode auf diese Weise zu schreiben.
Tamas Czinege
20
Ich wünschte, es gäbe nur zwei Typen in der Sprache / Datenbank / ... Welt: Nummer und Zeichenfolge :)
Liao
5
Das Schreiben von Netzwerkcode ist überhaupt nicht viel schwieriger. BTW InputStream.read () gibt ein vorzeichenloses Byte zurück, beispielsweise kein vorzeichenbehaftetes, sodass das Netzwerkbeispiel meiner Meinung nach eine Verwirrung darstellt. Es ist nur verwirrend, wenn Sie davon ausgehen, dass sich das Schreiben eines signierten Werts vom Schreiben eines nicht signierten Werts unterscheidet. dh wenn Sie nicht wirklich wissen, was auf Byte-Ebene passiert.
Peter Lawrey
19
@ZachSaw - Ich habe auch eine doppelte Aufnahme gemacht, als ich sah, wie ein Sprachdesigner dieses Zitat machte. Es gibt nichts Einfacheres als eine vorzeichenlose Ganzzahl. Ganzzahlen mit Vorzeichen sind kompliziert. Besonders wenn man das Bit-Twiddling auf Transistorebene betrachtet. Und wie verschiebt sich eine vorzeichenbehaftete Ganzzahl? Ich musste zu dem Schluss kommen, dass der Designer von Java ein ernstes Problem beim Verständnis der booleschen Logik hat.
PP.
8
Für mich wird es schwieriger, eine Bildverarbeitung durchzuführen, bei der Bilder bytekeinen geraden 140Graupegel liefern können, sondern einen -116, den Sie benötigen & 0xff, um den richtigen Wert zu erhalten.
Matthieu

Antworten:

193

Dies ist aus einem Interview mit Gosling und anderen über die Einfachheit:

Gosling: Für mich als Sprachdesigner, den ich heutzutage nicht wirklich als solche bezeichne, bedeutete "einfach", dass J. Random Developer die Spezifikation im Kopf behalten könnte. Diese Definition besagt, dass Java zum Beispiel nicht Java ist - und tatsächlich haben viele dieser Sprachen viele Eckfälle, Dinge, die niemand wirklich versteht. Fragen Sie jeden C-Entwickler nach unsigned, und schon bald stellen Sie fest, dass fast kein C-Entwickler wirklich versteht, was mit unsigned und vorzeichenloser Arithmetik vor sich geht. Solche Dinge machten C komplex. Der sprachliche Teil von Java ist meiner Meinung nach ziemlich einfach. Die Bibliotheken müssen Sie nachschlagen.

Uri
quelle
222
Ich werde Gosling hier mit einem konkreten Beispiel widersprechen müssen (von CLR nicht weniger). Was ist verwirrender, wenn einem Array ein vorzeichenbehafteter ganzzahliger Wert oder eine vorzeichenlose Länge zugewiesen wird? Es ist unmöglich, dass ein Array eine negative Länge hat, aber unsere API zeigt an, dass dies möglich ist.
JaredPar
18
Das Argument, Java einfach zu machen, ist Teil dessen, was uns in das ganze Chaos mit dem Mangel an Vorlagen gebracht hat, die sie schließlich in die Sprache gebracht haben, weil die Alternativen so umständlich waren. Ich denke, dass man unsignierte Ints mit einer geeigneten Klasse unterstützen könnte, es braucht aber keine Prims
Uri
59
Wenn Java vorzeichenlose Ganzzahlen benötigt, weil Array-Indizes nicht negativ sein können, benötigt es auch Unterbereiche (a la Pascal), da ein Array-Index nicht größer als die Array-Größe sein kann.
Wayne Conrad
81
Okay, er hat nur die Vorteile erklärt, keine nicht signierten Typen zu haben. Jetzt zählen wir die Nachteile ...
Moshe Revah
83
Ich bevorzuge die Einfachheit des Codes gegenüber der Einfachheit der Sprache. Deshalb hasse ich Java.
Pijusn
50

Wenn ich zwischen den Zeilen lese, denke ich, dass die Logik ungefähr so ​​war:

  • Im Allgemeinen wollten die Java-Designer das Repertoire der verfügbaren Datentypen vereinfachen
  • Für alltägliche Zwecke waren sie der Ansicht, dass signierte Datentypen am häufigsten benötigt werden
  • Für die Implementierung bestimmter Algorithmen wird manchmal eine vorzeichenlose Arithmetik benötigt, aber die Art von Programmierern, die solche Algorithmen implementieren würden, hätte auch das Wissen, um vorzeichenlose Arithmetik mit vorzeichenbehafteten Datentypen zu "umgehen"

Meistens würde ich sagen, dass es eine vernünftige Entscheidung war. Möglicherweise hätte ich:

  • Byte ohne Vorzeichen erstellt oder zumindest signierte / nicht signierte Alternativen, möglicherweise mit unterschiedlichen Namen, für diesen einen Datentyp bereitgestellt (signiert zu machen ist gut für die Konsistenz, aber wann benötigen Sie jemals ein signiertes Byte?)
  • 'short' abgeschafft (wann haben Sie zuletzt 16-Bit-Arithmetik mit Vorzeichen verwendet?)

Trotzdem sind Operationen mit vorzeichenlosen Werten bis zu 32 Bit mit ein wenig Kludging nicht allzu schlecht, und die meisten Leute benötigen keine vorzeichenlose 64-Bit-Division oder einen vorzeichenlosen Vergleich.

Neil Coffey
quelle
2
Ich hätte auch gerne vorzeichenlose Bytes, aber ich vermute, dass der Vorteil der vollständigen Konsistenz zwischen den ganzzahligen Typen die Bequemlichkeit überwiegt, die vorzeichenlose Bytes bringen würden.
Alan Moore
64
"Für alltägliche Zwecke waren sie der Ansicht, dass signierte Datentypen am häufigsten benötigt werden." In meinem C ++ - Code denke ich mehr als oft: "Warum um alles in der Welt verwende ich hier eine vorzeichenbehaftete Ganzzahl anstelle einer vorzeichenlosen?!". Ich habe das Gefühl, dass "signiert" eher die Ausnahme als die Regel ist (natürlich hängt es von der Domäne ab, aber es gibt einen Grund, warum positive ganze Zahlen natürliche Zahlen genannt werden ;-)).
Luc Touraille
15
Wenn ich bei der Bildverarbeitung davon ausgehe, dass Bytes nicht signiert sind (wie es sein sollte), habe ich stundenlang mit dem Debuggen verbracht.
Helin Wang
7
Sie shortwären überrascht, wie oft verwendet wird - Defltate / Gzip / Inflate-Algorithmen sind 16-Bit-Algorithmen und sie hängen stark von Shorts ab ... oder zumindest short[][zugegebenermaßen sind sie nativ - dennoch enthalten Java-Impl des Algorithmus Terrabytes an Daten]. Letzteres ( short[]) hat einen erheblichen Vorteil, int[]da es zweimal weniger Speicher und weniger Speicher benötigt = bessere Caching-Eigenschaften, viel bessere Leistung.
Bests
8
In einer bestimmten Anwendung sollten Sie jedoch messen, ob die Verwendung von Shorts zu einer besseren Leistung führt, anstatt davon auszugehen, dass dies der Fall ist. Es ist möglich, dass das zusätzliche Jiggery-Pokery, das erforderlich ist, um Shorts anstelle von Ints zu manipulieren (was normalerweise der Typ ist, den der Prozessor gerne verwendet), tatsächlich die Leistung in einer bestimmten Anwendung beeinträchtigt. Nicht immer, aber Sie sollten testen, nicht annehmen.
Neil Coffey
19

Dies ist eine ältere Frage, und Pat hat Char kurz erwähnt. Ich dachte nur, ich sollte sie für andere erweitern, die sich das später ansehen werden. Schauen wir uns die primitiven Java-Typen genauer an:

byte - 8-Bit-Ganzzahl mit Vorzeichen

short - 16-Bit-Ganzzahl mit Vorzeichen

int - 32-Bit-Ganzzahl mit Vorzeichen

long - 64-Bit-Ganzzahl mit Vorzeichen

char - 16-Bit-Zeichen (vorzeichenlose Ganzzahl)

Obwohl Arithmetik charnicht unterstützt wird unsigned, kann sie im Wesentlichen als unsignedGanzzahl behandelt werden . Sie müssten explizit arithmetische Operationen zurückverwandeln char, aber es bietet Ihnen die Möglichkeit, unsignedZahlen anzugeben .

char a = 0;
char b = 6;
a += 1;
a = (char) (a * b);
a = (char) (a + b);
a = (char) (a - 16);
b = (char) (b % 3);
b = (char) (b / a);
//a = -1; // Generates complier error, must be cast to char
System.out.println(a); // Prints ? 
System.out.println((int) a); // Prints 65532
System.out.println((short) a); // Prints -4
short c = -4;
System.out.println((int) c); // Prints -4, notice the difference with char
a *= 2;
a -= 6;
a /= 3;
a %= 7;
a++;
a--;

Ja, es gibt keine direkte Unterstützung für vorzeichenlose Ganzzahlen (natürlich müsste ich die meisten meiner Operationen nicht wieder in char umwandeln, wenn es direkte Unterstützung gäbe). Es gibt jedoch sicherlich einen vorzeichenlosen primitiven Datentyp. Ich hätte gerne auch ein Byte ohne Vorzeichen gesehen, aber ich denke, die Speicherkosten zu verdoppeln und stattdessen char zu verwenden, ist eine praktikable Option.


Bearbeiten

Mit JDK8 gibt es neue APIs für Longund, Integerdie Hilfsmethoden für die Behandlung von longund intWerten als vorzeichenlose Werte bereitstellen .

  • compareUnsigned
  • divideUnsigned
  • parseUnsignedInt
  • parseUnsignedLong
  • remainderUnsigned
  • toUnsignedLong
  • toUnsignedString

Darüber hinaus bietet Guava eine Reihe von Hilfsmethoden, mit denen ähnliche Aufgaben bei Ganzzahlentypen ausgeführt werden können, um die Lücke zu schließen, die durch den Mangel an nativer Unterstützung für unsignedGanzzahlen entsteht.

Jyro117
quelle
2
Ist jedoch charzu klein long, um beispielsweise die Arithmetik zu unterstützen .
3
Dies könnte ein Nachteil von Java sein
In der Hoffnung, dass sie vorzeichenlose Werte für Bytes unterstützen. Erleichtert die Arbeit.
Mischungz
15

Java hat vorzeichenlose Typen oder mindestens einen: char ist ein vorzeichenloser Short. Was auch immer Gosling vorbringt, es ist wirklich nur seine Unwissenheit, warum es keine anderen Typen ohne Vorzeichen gibt.

Auch kurze Typen: Shorts werden ständig für Multimedia verwendet. Der Grund dafür ist, dass Sie 2 Samples in ein einzelnes 32-Bit-Long ohne Vorzeichen einpassen und viele Operationen vektorisieren können. Gleiches gilt für 8-Bit-Daten und vorzeichenloses Byte. Sie können 4 oder 8 Samples zur Vektorisierung in ein Register einfügen.

klopfen
quelle
37
Ja, ich bin sicher, dass Gosling Java im Vergleich zu Ihnen sehr ignoriert.
Jakeboxer
Ermöglicht Java die direkte Ausführung von Arithmetik für vorzeichenlose Byte-Mengen oder werden Werte immer heraufgestuft? Ein vorzeichenloser Typ für die Speicherung zu haben, aber immer eine Arithmetik für einen vorzeichenbehafteten Typ durchzuführen, der groß genug ist, um ihn aufzunehmen, funktioniert semantisch gut, würde jedoch dazu führen, dass Operationen mit vorzeichenlosen Typen, die dieselbe Größe wie "normale" Ganzzahlen haben, teurer werden.
Supercat
2
Es ist ein schlechter Stil charfür alles andere als Charaktere.
Starblue
5
@starblue Natürlich ist es, aber es ist ein Hack, um eine Einschränkung der Sprache
Basic
14

Sobald mit und ohne Vorzeichen Ints gemischt Dinge in einem Ausdruck starten chaotisch zu bekommen und Sie wahrscheinlich werden Informationen verlieren. Das Beschränken von Java auf signierte Ints klärt die Dinge nur wirklich auf. Ich bin froh, dass ich mich nicht um das gesamte signierte / nicht signierte Geschäft kümmern muss, obwohl ich manchmal das 8. Bit in einem Byte verpasse.

Bombe
quelle
12
Zum Mischen von signierten / nicht signierten Typen: Sie könnten vorzeichenlose Typen haben, das Mischen jedoch nicht zulassen (oder explizite Casts erfordern). Immer noch nicht klar, ob es notwendig ist.
Sleske
2
In C ++ muss man static_castviel herumstreuen, um sie zu mischen. Es ist in der Tat chaotisch.
Raedwald
4
Das 8. Bit ist da, es versucht nur, sich als Zeichen zu verstecken.
Starblue
Nur bei Typen mit 32 Bit oder mehr wird es chaotisch. Ich sehe keinen Grund, warum Java nicht bytewie in Pascal hätte signiert werden sollen.
Supercat
12
Besuchen Sie mich, wenn Sie Probleme mit der Bildverarbeitung in Java haben und erwarten, dass Bytes ohne Vorzeichen sind. Dann wissen Sie, dass & 0xFFbei jeder Byte-to-Int-Promotion der Code noch chaotischer wird.
Bit2shift
12

http://skeletoncoder.blogspot.com/2006/09/java-tutorials-why-no-unsigned.html

Dieser Typ sagt, weil der C-Standard Operationen definiert, bei denen vorzeichenlose und vorzeichenbehaftete Ints als vorzeichenlos behandelt werden. Dies könnte dazu führen, dass negativ vorzeichenbehaftete Ganzzahlen in ein großes vorzeichenloses int übergehen und möglicherweise Fehler verursachen.

Akatakritos
quelle
34
Java-Ganzzahlen mit Vorzeichen rollen ebenfalls herum. Ich verstehe deinen Standpunkt nicht.
foo
8
@foo: Vorzeichenbehaftete Ganzzahlen müssen groß werden, bevor sie Probleme verursachen. Im Gegensatz dazu kann es in C -1schwierig sein , eine negative ganze Zahl - gerade - mit einer vorzeichenlosen Menge - sogar null - zu vergleichen.
Supercat
Es ist schade, dass Java keine vorzeichenlosen Typen enthalten konnte, aber mit einer begrenzten Anzahl von Konvertierungen und gemischten Operatoren (etwas analog zu der Art und Weise, wie man in C einem Zeiger 5 hinzufügen kann, aber einen Zeiger nicht mit 5 vergleichen kann). . Die Idee, dass die Verwendung eines Operators für gemischte Typen, wenn eine implizite Umwandlung vorhanden ist, die implizite Verwendung dieser Umwandlung erzwingen (und den nachfolgenden Typ als Ergebnistyp verwenden sollte), steht im Mittelpunkt vieler zweifelhafter Entwurfsentscheidungen sowohl in .NET als auch in .NET Java.
Supercat
4
Nicht auf Ihre Antwort zu schimpfen, sondern ein -1"unbekanntes" Alter zu haben (wie der Artikel andeutet), ist eines der klassischen Beispiele für "Code-Geruch" . Wenn Sie beispielsweise berechnen möchten, "wie viel Alice ist älter als Bob?" Und A = 25 und B = -1, erhalten Sie eine Antwort, ±26die einfach falsch ist. Der richtige Umgang mit unbekannten Werten ist eine Art Option<TArg>Wann Some(25) - Nonewürde zurückkehren None.
Bytebuster
11

Ich denke, Java ist in Ordnung, das Hinzufügen von unsignierten würde es ohne großen Gewinn erschweren. Selbst mit dem vereinfachten Ganzzahlmodell wissen die meisten Java-Programmierer nicht, wie sich die grundlegenden numerischen Typen verhalten - lesen Sie einfach das Buch Java Puzzlers zu sehen, welche Missverständnisse Sie möglicherweise haben.

Wie für praktische Ratschläge:

  • Wenn Ihre Werte eine beliebige Größe haben und nicht passen int, verwenden Sie long. Wenn sie passen nicht in longGebrauch BigInteger.

  • Verwenden Sie die kleineren Typen nur für Arrays, wenn Sie Platz sparen müssen.

  • Wenn Sie genau 64/32/16/8 Bits benötigen, verwenden Sie long/ int/ short/ byteund machen Sie sich keine Gedanken mehr über das Vorzeichenbit, außer für Division, Vergleich, Rechtsverschiebung und Casting.

Siehe auch diese Antwort zum Thema "Portieren eines Zufallszahlengenerators von C nach Java".

Sternenblau
quelle
5
Ja, um nach rechts zu wechseln, müssen Sie zwischen >>und >>>für signiert bzw. nicht signiert wählen . Nach links zu schalten ist kein Problem.
Starblue
1
@starblue Funktioniert eigentlich >>>nicht für shortund byte. Zum Beispiel eher (byte)0xff>>>1Erträge 0x7fffffffals 0x7f. Ein weiteres Beispiel: byte b=(byte)0xff; b>>>=1;führt zu b==(byte)0xff. Natürlich können Sie dies tun, b=(byte)(b & 0xff >> 1);aber dies fügt eine weitere Operation hinzu (bitweise &).
CITBL
7
"... Selbst mit dem vereinfachten Modell wissen die meisten Java-Programmierer nicht, wie sich die grundlegenden numerischen Typen verhalten ..." Etwas in mir lehnt nur eine Sprache ab, die auf den kleinsten gemeinsamen Nenner abzielt.
Basic
Die erste Zeile in Ihrer Antwort, über mehr Komplikationen und wenig Gewinn, ist genau das, worauf ich 6 Jahre später in meinem Artikel näher
eingegangen
1
@Nayuki Dein Artikel ist wirklich schön. Nur eine kleine Bemerkung, ich würde das Hinzufügen von 0x80000000 für Vergleichsoperatoren anstelle von XOR verwenden, da es erklärt, warum es funktioniert, es verschiebt den zusammenhängenden Bereich, in dem der Vergleich stattfindet, von -MAXINT auf 0. Bitweise ist sein Effekt genau der gleiche.
Starblue
6

Mit JDK8 hat es einige Unterstützung für sie.

Trotz Goslings Bedenken sehen wir möglicherweise noch volle Unterstützung für nicht signierte Typen in Java.

John Hascall
quelle
12
aka "Also die Leute benutzen es wirklich und wir haben uns geirrt, es zunächst nicht einzuschließen" - aber wir vertrauen Java-Entwicklern immer noch nicht ganz, ob eine Variable signiert ist oder nicht - also werden wir sie nicht implementieren in der VM oder als Typen, die ihren signierten Cousins ​​entsprechen.
Basic
6

Ich weiß, dass dieser Beitrag zu alt ist. Für Ihr Interesse können Sie in Java 8 und höher den intDatentyp jedoch verwenden, um eine vorzeichenlose 32-Bit-Ganzzahl darzustellen, die einen Mindestwert von 0 und einen Höchstwert von 2 32 −1 hat. Verwenden , um die IntegerKlasse zu verwenden intDatentyp als unsigned integer und statische Methoden wie compareUnsigned(), divideUnsigned()usw. wurde die hinzugefügt IntegerKlasse , um die arithmetischen Operationen für ganze Zahlen ohne Vorzeichen zu unterstützen.

Morteza Adi
quelle
4

Ich habe Geschichten gehört, dass sie in der Nähe der ursprünglichen Java-Version enthalten sein sollten. Eiche war der Vorläufer von Java, und in einigen Spezifikationsdokumenten wurden usignierte Werte erwähnt. Leider haben diese es nie in die Java-Sprache geschafft. Soweit jemand herausfinden konnte, wurde er einfach nicht implementiert, wahrscheinlich aus Zeitgründen.

Rob Ottaway
quelle
Dies wäre in Ordnung ... außer die Beweise aus dem Gosling-Interview deuten darauf hin, dass vorzeichenlose Ganzzahlen (abgesehen von char) weggelassen wurden, weil die Designer sie für eine schlechte Idee hielten ... angesichts der Ziele der Sprache.
Stephen C
Es ist eine gute Idee, Augenzeugenaussagen niemals zu viel Wert beizumessen, wenn auch dokumentarische Beweise vorliegen.
user7610
4

Ich habe einmal einen C ++ - Kurs mit jemandem im C ++ - Standardkomitee besucht, der implizierte, dass Java die richtige Entscheidung getroffen hat, um vorzeichenlose Ganzzahlen zu vermeiden, da (1) die meisten Programme, die vorzeichenlose Ganzzahlen verwenden, genauso gut mit vorzeichenbehafteten Ganzzahlen umgehen können und dies natürlicher ist Die Art und Weise, wie Menschen denken, und (2) die Verwendung von Ganzzahlen ohne Vorzeichen führen zu vielen einfach zu erstellenden, aber schwer zu debuggenden Problemen wie dem Überlauf der Ganzzahlarithmetik und dem Verlust signifikanter Bits beim Konvertieren zwischen vorzeichenbehafteten und vorzeichenlosen Typen. Wenn Sie fälschlicherweise 1 mit vorzeichenbehafteten Ganzzahlen von 0 subtrahieren, stürzt Ihr Programm häufig schneller ab und es ist einfacher, den Fehler zu finden, als wenn es sich um 2 ^ 32 - 1 handelt, und Compiler, statische Analysetools und Laufzeitprüfungen müssen dies tun Angenommen, Sie wissen, was Sie tun, da Sie sich für die Verwendung von Arithmetik ohne Vorzeichen entschieden haben. Ebenfalls,

Vor langer Zeit, als der Speicher begrenzt war und die Prozessoren nicht automatisch mit 64 Bit gleichzeitig arbeiteten, zählte jedes Bit viel mehr. Daher war es viel häufiger wichtig, vorzeichenbehaftete oder vorzeichenlose Bytes oder Kurzschlüsse zu haben, und dies war offensichtlich die richtige Entwurfsentscheidung. Heutzutage ist es in fast allen regulären Programmierfällen mehr als ausreichend, nur ein vorzeichenbehaftetes int zu verwenden, und wenn Ihr Programm wirklich Werte verwenden muss, die größer als 2 ^ 31 - 1 sind, möchten Sie oft sowieso nur ein langes. Wenn Sie erst einmal Longs verwendet haben, ist es noch schwieriger, einen Grund zu finden, warum Sie mit 2 ^ 63 - 1 positiven ganzen Zahlen wirklich nicht auskommen können. Wann immer wir zu 128-Bit-Prozessoren gehen, wird es noch weniger ein Problem sein.

Jonathan
quelle
2

Ihre Frage lautet "Warum unterstützt Java keine vorzeichenlosen Ints"?

Und meine Antwort auf Ihre Frage lautet, dass Java möchte, dass alle primitiven Typen: Byte , Zeichen , Kurz , Int und Lang genau wie in Assembly als Byte , Wort , Dword und Qword behandelt werden und die Java-Operatoren signiert werden Operationen für alle primitiven Typen außer char , aber nur für char sind sie nur 16-Bit ohne Vorzeichen.

Daher wird angenommen, dass statische Methoden auch für 32- und 64-Bit- Operationen ohne Vorzeichen sind .

Sie benötigen die letzte Klasse, deren statische Methoden für die vorzeichenlosen aufgerufen werden können Operationen .

Sie können diese letzte Klasse erstellen, sie beliebig nennen und ihre statischen Methoden implementieren.

Wenn Sie keine Ahnung haben, wie die statischen Methoden implementiert werden sollen, klicken Sie auf diesen Link kann Ihnen helfen.

Meiner Meinung nach ist Java C ++ überhaupt nicht ähnlich , wenn es weder vorzeichenlose Typen noch unterstützt Überladen von Operatoren denke ich, dass Java als völlig andere Sprache als C ++ und C behandelt werden sollte.

Es ist übrigens auch im Namen der Sprachen völlig anders.

Daher empfehle ich in Java nicht, Code ähnlich wie C ++ einzugeben, und ich empfehle überhaupt nicht, Code ähnlich C ++ einzugeben, da Sie dann in Java nicht in der Lage sind, das zu tun, was Sie als Nächstes in C ++ tun möchten. dh der Code wird überhaupt nicht mehr C ++ sein und für mich ist es schlecht, so zu codieren, um den Stil in der Mitte zu ändern.

Ich empfehle, statische Methoden auch für die signierten Operationen zu schreiben und zu verwenden, sodass Sie in der Codemischung aus Operatoren und statischen Methoden sowohl für signierte als auch für nicht signierte Operationen nichts sehen, es sei denn, Sie benötigen nur signierte Operationen im Code, und das ist in Ordnung Verwenden Sie nur die Operatoren.

Außerdem empfehle ich, die Verwendung von kurzen , int und langen primitiven Typen zu vermeiden und Wort , Wort und Wort zu verwenden stattdessen zu verwenden, und Sie werden die statischen Methoden für vorzeichenlose Operationen und / oder vorzeichenbehaftete Operationen aufrufen, anstatt Operatoren zu verwenden.

Wenn Sie nur signierte Operationen ausführen und die Operatoren nur im Code verwenden möchten, ist es in Ordnung, diese primitiven Typen short , int und long zu verwenden .

Eigentlich tun es Wort , Wort und Wort nicht existieren in der Sprache, aber Sie können für jede neue Klasse erstellen und mit der Durchführung jeder sollte sehr einfach sein:

Die Klasse Wort hält die primitive Art kurz nur, die Klasse dword den Urtyp hält int nur und die Klasse qword hält den Urtyp lange nur. Jetzt können Sie alle vorzeichenlosen und vorzeichenbehafteten Methoden als statisch oder nicht nach Ihrer Wahl in jeder Klasse implementieren, dh alle 16-Bit-Operationen, sowohl vorzeichenlos als auch signiert, indem Sie der Wortklasse Bedeutungsnamen geben , alle 32-Bit-Operationen sowohl vorzeichenlos als auch signiert durch Angabe von Bedeutungsnamen für die Dword- Klasse und alle 64-Bit-Operationen ohne Vorzeichen und signiert durch Angabe von Bedeutungsnamen für die qword- Klasse.

Wenn Sie nicht gerne zu viele verschiedene Namen für jede Methode angeben, können Sie in Java immer eine Überladung verwenden. Gut zu lesen, dass Java das nicht auch entfernt hat!

Wenn Sie Methoden anstelle von Operatoren für vorzeichenbehaftete 8-Bit-Operationen und Methoden für vorzeichenlose 8-Bit-Operationen ohne Operatoren wünschen, können Sie die Byte- Klasse erstellen (beachten Sie, dass der erste Buchstabe 'B' Großbuchstaben ist, dies ist also nicht der primitives Typbyte ) und implementieren Sie die Methoden in dieser Klasse.

Über das Übergeben von Werten und Übergeben von Referenzen:

Wenn ich mich nicht irre, wie in C #, werden primitive Objekte natürlich als Wert übergeben, aber Klassenobjekte werden natürlich als Referenz übergeben, was bedeutet, dass Objekte vom Typ Byte , Wort , Dword und Qword als Referenz und nicht als Wert übergeben werden standardmäßig. Ich wünschte, Java hätte Strukturobjekte wie C #, so dass alle Bytes , Wörter , Wörter und QWords so implementiert werden könnten, dass sie Struktur statt Klasse sindDaher wurden sie standardmäßig als Wert und nicht standardmäßig als Referenz übergeben, wie jedes Strukturobjekt in C #, wie die primitiven Typen, als Wert und nicht standardmäßig als Referenz, sondern weil Java schlechter ist als C # und wir haben Um damit umzugehen, gibt es nur Klassen und Schnittstellen, die standardmäßig als Referenz und nicht als Wert übergeben werden. Also , wenn Sie weitergeben wollen Byte , Wort , dword und qword Objekte von Wert und nicht nach, wie jede andere Klasse Objekt in Java und auch in C #, müssen Sie einfach die Kopie Konstruktor verwenden und das ist es.

Das ist die einzige Lösung, über die ich nachdenken kann. Ich wünschte nur, ich könnte die primitiven Typen einfach in word, dword und qword eingeben, aber Java unterstützt weder typedef noch die Verwendung überhaupt, im Gegensatz zu C #, das die Verwendung unterstützt , was dem typedef des C entspricht.

Informationen zur Ausgabe:

Für dieselbe Folge von Bits können Sie sie auf verschiedene Arten drucken: Als binär, als Dezimalzahl (wie die Bedeutung von% u in C printf), als Oktal (wie die Bedeutung von% o in C printf), als Hexadezimalzahl (wie) die Bedeutung von% x in C printf) und als Ganzzahl (wie die Bedeutung von% d in C printf).

Beachten Sie, dass C printf den Typ der Variablen, die als Parameter an die Funktion übergeben werden, nicht kennt. Daher kennt printf den Typ jeder Variablen nur von dem char * -Objekt, das an den ersten Parameter der Funktion übergeben wird.

In jeder der Klassen: Byte , Wort , Dword und Qword können Sie die Druckmethode implementieren und die Funktionalität von printf abrufen. Auch wenn der primitive Typ der Klasse signiert ist, können Sie ihn dennoch als nicht signiert drucken, indem Sie einem Algorithmus folgen logische und Verschiebungsoperationen, damit die Ziffern in die Ausgabe gedruckt werden.

Leider zeigt der Link, den ich Ihnen gegeben habe, nicht, wie diese Druckmethoden implementiert werden, aber ich bin sicher, dass Sie nach den Algorithmen suchen können, die Sie zum Implementieren dieser Druckmethoden benötigen.

Das ist alles, was ich auf Ihre Frage beantworten und Ihnen vorschlagen kann.


quelle
MASM (Microsoft Assembler) und Windows definieren BYTE, WORD, DWORD, QWORD als vorzeichenlose Typen. Für MASM sind SBYTE, SWORD, SDWORD, SQWORD die vorzeichenbehafteten Typen.
rcgldr
1

Weil unsignedTyp rein böse ist.

Die Tatsache, dass in C unsigned - intproduziert, unsignedist noch böser.

Hier ist eine Momentaufnahme des Problems, das mich mehr als einmal verbrannt hat:

// We have odd positive number of rays, 
// consecutive ones at angle delta from each other.
assert( rays.size() > 0 && rays.size() % 2 == 1 );

// Get a set of ray at delta angle between them.
for( size_t n = 0; n < rays.size(); ++n )
{
    // Compute the angle between nth ray and the middle one.
    // The index of the middle one is (rays.size() - 1) / 2,
    // the rays are evenly spaced at angle delta, therefore
    // the magnitude of the angle between nth ray and the 
    // middle one is: 
    double angle = delta * fabs( n - (rays.size() - 1) / 2 ); 

    // Do something else ...
}

Haben Sie den Fehler schon bemerkt? Ich gebe zu, ich habe es erst gesehen, nachdem ich mit dem Debugger eingestiegen bin.

Da nes sich um einen vorzeichenlosen Typ handelt, wird size_tder gesamte Ausdruck n - (rays.size() - 1) / 2als ausgewertet unsigned. Dieser Ausdruck soll eine vorzeichenbehaftete Position des ndritten Strahls vom mittleren Strahl sein: Der erste Strahl vom mittleren Strahl auf der linken Seite hätte die Position -1, der erste Strahl auf der rechten Seite hätte die Position +1 usw. Danach Wenn deltaich den abs-Wert nehme und mit dem Winkel multipliziere, erhalte ich den Winkel zwischen dem nStrahl und dem mittleren.

Leider enthielt der obige Ausdruck für mich das Böse ohne Vorzeichen, und anstatt beispielsweise -1 zu bewerten, bewertete er 2 ^ 32-1. Die anschließende Konvertierung doublebesiegelte den Fehler.

Nach ein oder zwei Fehlern, die durch den Missbrauch von unsignedArithmetik verursacht wurden, muss man sich fragen, ob das zusätzliche Bit, das man bekommt, die zusätzliche Mühe wert ist. Ich versuche so weit wie möglich, die Verwendung von unsignedTypen in der Arithmetik zu vermeiden , obwohl ich sie immer noch für nicht-arithmetische Operationen wie Binärmasken verwende.

Michael
quelle
Das Hinzufügen von "unsigned long" zu Java wäre umständlich. Das Hinzufügen kleinerer vorzeichenloser Typen sollte jedoch kein Problem darstellen. Insbesondere Typen, die kleiner als "int" sind, hätten leicht behandelt werden können, indem sie auf numerisch offensichtliche Weise zu "int" heraufgestuft wurden, und "unsigned int" hätte behandelt werden können, indem gesagt wurde, dass Operationen, die ein signiertes int und ein nicht signiertes int beinhalten, befördert werden beide Operanden zu "lang". Die einzige Problemsituation wären Operationen mit einer vorzeichenlosen langen und einer vorzeichenbehafteten Menge, da es keinen Typ geben würde, der alle Werte beider Operanden darstellen könnte.
Supercat
@supercat: Wenn bei jeder Operation unsignedkonvertiert wird, intwozu dann unsigned? Es wird keine Funktionalität haben, von der man unterscheiden kann short. Und wenn Sie intnur auf gemischte Vorgänge wie unsigned+intoder unsigned+floatumstellen, haben Sie immer noch das Problem ((unsigned)25-(unsigned)30)*1.0 > 0, was eine Hauptursache für unsignedFehler im Zusammenhang ist.
Michael
Viele Operationen mit nicht signierten Typen würden zu "lang" führen. Das Erfordernis expliziter Umwandlungen beim Speichern des Ergebnisses in vorzeichenlosen Typen würde fast die gleichen Probleme verursachen wie bei Kurz- und Byte-Typen, aber wenn der Typ hauptsächlich ein Speicherformat und kein Berechnungsformat ist, sollte dies kein Problem sein. In jedem Fall sollten vorzeichenlose Typen, die kürzer als "int" sind, einfach ohne Schwierigkeiten auf "int" hochgestuft werden können.
Supercat
3
Ich mag diese Antwort nicht, weil sie das Argument "Ganzzahlen ohne Vorzeichen sind böse und sollten nicht existieren, weil sie niemals signiert werden können" verwendet. Jeder, der versucht, von einer vorzeichenlosen Ganzzahl zu subtrahieren, sollte dies bereits wissen. Und was die Lesbarkeit betrifft, ist C nicht gerade dafür bekannt, dass es leicht zu befolgen ist. Darüber hinaus ist das (Halb-) Argument "das zusätzliche Bit ist die zusätzliche Mühe nicht wert" ebenfalls sehr schwach. Ist die Fehlerbehandlung exit(1);wirklich "die zusätzliche Mühe wert"? Ist es nicht wirklich die Sicherheit wert, große Dateien öffnen zu können, die weniger erfahrene Java-Programmierer nicht vermasseln unsigned?
yyny
2
Das einzig Böse, das ich in diesem Code sehe, ist n - (rays.size() - 1) / 2. Sie sollten immer binäre Operatoren in Klammern setzen, da der Leser des Codes nichts über die Reihenfolge der Operationen in einem Computerprogramm annehmen muss. Nur weil wir herkömmlicherweise a + b c = a + (b c) sagen, heißt das nicht, dass Sie dies beim Lesen von Code annehmen können. Darüber hinaus sollte die Berechnung außerhalb der Schleife definiert werden, damit sie ohne die vorhandene Schleife getestet werden kann. Dies ist ein Fehler, bei dem nicht sichergestellt wird, dass Ihre Typen ausgerichtet sind, und nicht das Problem vorzeichenloser Ganzzahlen. In C liegt es an Ihnen, sicherzustellen, dass Ihre Typen übereinstimmen.
Dmitry
0

Es gibt ein paar Juwelen in der 'C'-Spezifikation, die Java aus pragmatischen Gründen fallen ließ, die sich aber langsam mit der Nachfrage der Entwickler zurückziehen (Schließungen usw.).

Ich erwähne eine erste, weil sie mit dieser Diskussion zusammenhängt; das Festhalten von Zeigerwerten an vorzeichenlose Ganzzahlarithmetik. Und in Bezug auf dieses Thread-Thema die Schwierigkeit, die Semantik ohne Vorzeichen in der signierten Welt von Java beizubehalten.

Ich würde vermuten, wenn man ein Alter Ego von Dennis Ritchie dazu bringen würde, Goslings Designteam zu beraten, hätte es vorgeschlagen, Signed eine "Null im Unendlichen" zu geben, so dass alle Adressoffset-Anforderungen zuerst ihre ALGEBRAISCHE RINGGRÖSSE addieren würden, um negative Werte zu vermeiden.

Auf diese Weise kann ein auf das Array geworfener Offset niemals ein SEGFAULT erzeugen. Zum Beispiel in einer gekapselten Klasse, die ich RingArray of Doubles nenne und die ein vorzeichenloses Verhalten benötigt - im Kontext einer "selbstrotierenden Schleife":

// ...
// Housekeeping state variable
long entrycount;     // A sequence number
int cycle;           // Number of loops cycled
int size;            // Active size of the array because size<modulus during cycle 0
int modulus;         // Maximal size of the array

// Ring state variables
private int head;   // The 'head' of the Ring
private int tail;   // The ring iterator 'cursor'
// tail may get the current cursor position
// and head gets the old tail value
// there are other semantic variations possible

// The Array state variable
double [] darray;    // The array of doubles

// somewhere in constructor
public RingArray(int modulus) {
    super();
    this.modulus = modulus;
    tail =  head =  cycle = 0;
    darray = new double[modulus];
// ...
}
// ...
double getElementAt(int offset){
    return darray[(tail+modulus+offset%modulus)%modulus];
}
//  remember, the above is treating steady-state where size==modulus
// ...

Das obige RingArray würde niemals von einem negativen Index "bekommen", selbst wenn ein böswilliger Anforderer es versuchen würde. Denken Sie daran, dass es auch viele legitime Anfragen gibt, nach vorherigen (negativen) Indexwerten zu fragen.

NB: Der äußere% -Modul verweist auf legitime Anfragen, während der innere% -Modul offensichtliche Bosheit von Negativen maskiert, die negativer als -modul sind. Sollte dies jemals in einem Java + .. + 9 || erscheinen 8 + .. + spec, dann würde das Problem wirklich zu einem "Programmierer, der sich nicht selbst drehen kann".

Ich bin sicher, dass der sogenannte Java unsigned int 'Mangel' mit dem oben genannten Einzeiler ausgeglichen werden kann.

PS: Nur um der obigen RingArray-Verwaltung einen Kontext zu geben, hier eine Kandidaten-Set-Operation, die mit der obigen Get-Element-Operation übereinstimmt:

void addElement(long entrycount,double value){ // to be called only by the keeper of entrycount
    this.entrycount= entrycount;
    cycle = (int)entrycount/modulus;
    if(cycle==0){                       // start-up is when the ring is being populated the first time around
        size = (int)entrycount;         // during start-up, size is less than modulus so use modulo size arithmetic
        tail = (int)entrycount%size;    //  during start-up
    }
    else {
        size = modulus;
        head = tail;
        tail = (int)entrycount%modulus; //  after start-up
    }
    darray[head] = value;               //  always overwrite old tail
}
MKhomo
quelle
-2

Ich kann mir eine unglückliche Nebenwirkung vorstellen. In in Java eingebetteten Datenbanken beträgt die Anzahl der IDs, die Sie mit einem 32-Bit-ID-Feld haben können, 2 ^ 31, nicht 2 ^ 32 (~ 2 Milliarden, nicht ~ 4 Milliarden).

mike g
quelle
1
Er denkt wahrscheinlich an Arrays und kann keine negativen ganzen Zahlen als Indizes verwenden. Wahrscheinlich.
SK9
2
Wenn Felder in Datenbanken automatisch inkrementiert werden, sind sie oft verrückt.
Joshua
-8

Der Grund IMHO ist, dass sie zu faul sind / waren, um diesen Fehler zu implementieren / zu korrigieren. Es ist einfach absurd zu behaupten, dass C / C ++ - Programmierer unsigned, struct, union, bit flag ... nicht verstehen.

Sie haben mit einem einfachen / bash / java-Programmierer gesprochen, der kurz davor stand, a la C zu programmieren, ohne diese Sprache wirklich zu kennen, oder Sie sprechen nur aus Ihrem eigenen Kopf heraus. ;)

Wenn Sie sich jeden Tag mit dem Format von Datei oder Hardware befassen, beginnen Sie sich zu fragen, was zum Teufel sie gedacht haben.

Ein gutes Beispiel wäre hier der Versuch, ein vorzeichenloses Byte als selbstrotierende Schleife zu verwenden. Für diejenigen unter Ihnen, die den letzten Satz nicht verstehen, wie um alles in der Welt Sie sich selbst als Programmierer bezeichnen.

DC

Denis Co.
quelle
34
Nur für Kicks, Google die Phrase "selbstdrehende Schleife". Es ist klar , dass Denis Co die einzige Person auf der Welt ist, die es verdient, sich selbst als Programmierer zu bezeichnen :-)
Stephen C
6
Diese Antwort ist so schlecht, dass es lustig ist
Nayuki