Pourquoi Java n'inclut-il pas la prise en charge des entiers non signés?
Il me semble que c'est une omission étrange, étant donné qu'ils permettent d'écrire du code qui est moins susceptible de produire des débordements sur une entrée de taille inattendue.
De plus, l'utilisation d'entiers non signés peut être une forme d'auto-documentation, car ils indiquent que la valeur que l'entité non signée était censée contenir n'est jamais supposée être négative.
Enfin, dans certains cas, les entiers non signés peuvent être plus efficaces pour certaines opérations, telles que la division.
Quel est l'inconvénient de les inclure?
java
language-design
unsigned
integer
dsimcha
la source
la source
byte
ne peuvent pas donner un140
niveau de gris droit mais-116
dont vous avez besoin& 0xff
pour obtenir la valeur correcte.Réponses:
Ceci est tiré d'une interview avec Gosling et d'autres , sur la simplicité:
la source
En lisant entre les lignes, je pense que la logique était quelque chose comme ceci:
Surtout, je dirais que c'était une décision raisonnable. Eventuellement, j'aurais:
Pourtant, avec un peu de kludging, les opérations sur des valeurs non signées jusqu'à 32 bits ne sont pas trop mauvaises, et la plupart des gens n'ont pas besoin d'une division ou d'une comparaison 64 bits non signée.
la source
short
utilisation - les algorithmes defltate / gzip / inflate sont en 16 bits et ils reposent fortement sur des courts métrages ... ou du moinsshort[]
[certes, ils sont natifs - pourtant, l'impl java de l'algorithme contient des terrabytes de données]. Ce dernier (short[]
) a un avantage significatifint[]
car il prend deux fois moins de mémoire et moins de mémoire = meilleures propriétés de mise en cache, performances bien meilleures.C'est une question plus ancienne et Pat a brièvement mentionné le caractère, je pensais juste que je devrais développer cela pour d'autres qui examineront cela plus tard. Examinons de plus près les types primitifs Java:
byte
- Entier signé 8 bitsshort
- Entier signé 16 bitsint
- Entier signé 32 bitslong
- Entier signé 64 bitschar
- Caractère 16 bits (entier non signé)Bien
char
qu'il ne prenne pas en charge l'unsigned
arithmétique, il peut essentiellement être traité comme ununsigned
entier. Vous devrez reconstituer explicitement les opérations arithmétiqueschar
, mais cela vous permet de spécifier desunsigned
nombres.Oui, il n'y a pas de prise en charge directe pour les entiers non signés (évidemment, je n'aurais pas à reconvertir la plupart de mes opérations en char s'il y avait une prise en charge directe). Cependant, il existe certainement un type de données primitif non signé. J'aurais aimé voir également un octet non signé, mais je suppose que doubler le coût de la mémoire et utiliser à la place char est une option viable.
Éditer
Avec JDK8, il existe de nouvelles API pour
Long
etInteger
qui fournissent des méthodes d'assistance lors du traitementlong
et desint
valeurs en tant que valeurs non signées.compareUnsigned
divideUnsigned
parseUnsignedInt
parseUnsignedLong
remainderUnsigned
toUnsignedLong
toUnsignedString
De plus, Guava fournit un certain nombre de méthodes d'aide pour faire des choses similaires pour les types d'entiers, ce qui aide à combler l'écart laissé par le manque de prise en charge native des
unsigned
entiers.la source
char
est trop petit pour prendre en charge l'long
arithmétique, par exemple.Java a des types non signés, ou au moins un: char est un court non signé. Donc, quelle que soit l'excuse que Gosling lève, c'est vraiment son ignorance pourquoi il n'y a pas d'autres types non signés.
Aussi types courts: les shorts sont utilisés tout le temps pour le multimédia. La raison en est que vous pouvez ajuster 2 échantillons dans un seul long 32 bits non signé et vectoriser de nombreuses opérations. Même chose avec les données 8 bits et l'octet non signé. Vous pouvez insérer 4 ou 8 échantillons dans un registre pour la vectorisation.
la source
char
pour autre chose que pour les personnages.Dès que signés et non signés ints sont mélangés dans une expression de choses commencent à se salir et vous probablement allez perdre des informations. Limiter Java aux entrées signées ne fait que clarifier les choses. Je suis heureux de ne pas avoir à me soucier de l'ensemble de l'entreprise signée / non signée, bien que je manque parfois le 8e bit dans un octet.
la source
static_cast
beaucoup pour les mélanger. C'est en effet désordonné.byte
être signé comme c'était le cas en Pascal.& 0xFF
chaque promotion d'octet à entier rend le code encore plus compliqué.http://skeletoncoder.blogspot.com/2006/09/java-tutorials-why-no-unsigned.html
Ce type dit que la norme C définit les opérations impliquant des entrées non signées et signées à traiter comme non signées. Cela pourrait entraîner des entiers signés négatifs dans un grand entier non signé, ce qui pourrait entraîner des bogues.
la source
-1
n'importe quelle quantité non signée - même zéro.-1
âge "inconnu" (comme le suggère l'article) est l'un des exemples classiques de "l'odeur de code" . Par exemple, si vous voulez calculer "combien Alice est plus âgée que Bob?", Et A = 25 et B = -1, vous obtiendrez une réponse±26
qui est tout simplement fausse. La bonne gestion des valeurs inconnues est une sorte deOption<TArg>
quandSome(25) - None
reviendraitNone
.Je pense que Java est bien comme il est, l'ajout de non signé le compliquerait sans trop de gain. Même avec le modèle d'entier simplifié, la plupart des programmeurs Java ne savent pas comment se comportent les types numériques de base - il suffit de lire le livre Java Puzzlers pour voir quelles idées fausses vous pouvez avoir.
Quant aux conseils pratiques:
Si vos valeurs sont de taille quelque peu arbitraire et ne correspondent pas
int
, utilisezlong
. S'ils ne correspondent pas à l'long
utilisationBigInteger
.Utilisez les types plus petits uniquement pour les tableaux lorsque vous devez économiser de l'espace.
Si vous avez besoin d'exactement 64/32/16/8 bits, utilisez
long
/int
/short
/byte
et arrêtez de vous soucier du bit de signe, sauf pour la division, la comparaison, le décalage à droite et la conversion.Voir aussi cette réponse sur "le portage d'un générateur de nombres aléatoires de C vers Java".
la source
>>
et>>>
pour signé et non signé, respectivement. Décaler à gauche n'est pas un problème.>>>
fait, ne fonctionne pas pourshort
etbyte
. Par exemple, les(byte)0xff>>>1
rendements0x7fffffff
plutôt que0x7f
. Un autre exemple:byte b=(byte)0xff; b>>>=1;
entraînerab==(byte)0xff
. Bien sûr, vous pouvez le faire,b=(byte)(b & 0xff >> 1);
mais cela ajoute une opération de plus (bit à bit &).Avec JDK8 il a un certain support pour eux.
Nous pouvons encore voir un support complet des types non signés en Java malgré les inquiétudes de Gosling.
la source
Je sais que ce message est trop ancien; cependant, pour votre intérêt, dans Java 8 et versions ultérieures, vous pouvez utiliser le
int
type de données pour représenter un entier 32 bits non signé, qui a une valeur minimale de 0 et une valeur maximale de 2 32 -1. Utilisez laInteger
classe pour utiliserint
le type de données comme un entier non signé et des méthodes statiques commecompareUnsigned()
,divideUnsigned()
etc. ont été ajoutées à laInteger
classe pour prendre en charge les opérations arithmétiques pour les entiers non signés.la source
J'ai entendu des histoires selon lesquelles ils devaient être inclus près de la version Java d'origine. Oak était le précurseur de Java, et dans certains documents de spécification, il était fait mention de valeurs usignées. Malheureusement, ceux-ci ne sont jamais entrés dans le langage Java. Pour autant que quiconque ait pu comprendre qu'ils n'ont tout simplement pas été mis en œuvre, probablement en raison d'une contrainte de temps.
la source
char
) ont été laissés de côté parce que les concepteurs pensaient que c'était une mauvaise idée ... étant donné les objectifs du langage.Une fois, j'ai suivi un cours C ++ avec quelqu'un du comité des normes C ++ qui a laissé entendre que Java avait pris la bonne décision pour éviter d'avoir des entiers non signés car (1) la plupart des programmes qui utilisent des entiers non signés peuvent faire aussi bien avec des entiers signés et c'est plus naturel dans termes de la façon dont les gens pensent et (2) l'utilisation d'entiers non signés entraîne de nombreux problèmes faciles à créer mais difficiles à déboguer tels que le dépassement arithmétique des entiers et la perte de bits significatifs lors de la conversion entre les types signés et non signés. Si vous soustrayez par erreur 1 de 0 à l'aide d'entiers signés, votre programme se bloque souvent plus rapidement et facilite la recherche du bogue que s'il passe à 2 ^ 32-1, et les compilateurs et les outils d'analyse statique et les vérifications d'exécution doivent supposez que vous savez ce que vous faites puisque vous avez choisi d'utiliser l'arithmétique non signée. Aussi,
Il y a longtemps, lorsque la mémoire était limitée et que les processeurs ne fonctionnaient pas automatiquement sur 64 bits à la fois, chaque bit comptait beaucoup plus, donc avoir des octets ou des shorts signés vs non signés importait en fait beaucoup plus souvent et était évidemment la bonne décision de conception. Aujourd'hui, le simple fait d'utiliser un int signé est plus que suffisant dans presque tous les cas de programmation réguliers, et si votre programme a vraiment besoin d'utiliser des valeurs supérieures à 2 ^ 31 - 1, vous voulez souvent un long quand même. Une fois que vous êtes sur le territoire de l'utilisation des longs, il est encore plus difficile de trouver une raison pour laquelle vous ne pouvez vraiment pas vous en sortir avec 2 ^ 63 - 1 entiers positifs. Chaque fois que nous passerons à des processeurs 128 bits, ce sera encore moins un problème.
la source
Votre question est "Pourquoi Java ne prend-il pas en charge les entrées non signées"?
Et ma réponse à votre question est que Java veut que tous ses types primitifs: octet , char , short , int et long soient traités comme octet , mot , dword et qword , exactement comme dans l'assemblage, et les opérateurs Java sont signés opérations sur tous ses types primitifs à l'exception de char , mais uniquement sur char elles ne sont signées que sur 16 bits.
Ainsi , les méthodes statiques supposés être les non signés opérations aussi aux 32 et 64 bits.
Vous avez besoin de la classe finale, dont les méthodes statiques peuvent être appelées pour le unsigned opérations .
Vous pouvez créer cette classe finale, l'appeler comme vous voulez et implémenter ses méthodes statiques.
Si vous ne savez pas comment implémenter les méthodes statiques, alors ce lien peut vous aider.
À mon avis, Java est pas similaire à C ++ du tout , si elle ne supporte les types non signés , ni surcharge d'opérateur, donc je pense que Java doit être traité comme tout autre langage à la fois C ++ et de C.
Soit dit en passant, c'est complètement différent dans le nom des langues.
Donc, je ne recommande pas en Java de taper du code similaire à C et je ne recommande pas du tout de taper du code similaire à C ++, car en Java, vous ne pourrez pas faire ce que vous voulez faire ensuite en C ++, c'est-à-dire que le code ne continuera pas du tout à être en C ++ et pour moi, c'est mauvais de coder comme ça, de changer le style au milieu.
Je recommande d'écrire et d'utiliser des méthodes statiques également pour les opérations signées, de sorte que vous ne voyez pas dans le mélange de code des opérateurs et des méthodes statiques pour les opérations signées et non signées, à moins que vous n'ayez besoin que d'opérations signées dans le code, et c'est correct de utilisez uniquement les opérateurs.
Je recommande également d'éviter d'utiliser des types primitifs courts , int et longs , et d'utiliser le mot , dword et qword , et vous êtes sur le point d'appeler les méthodes statiques pour les opérations non signées et / ou les opérations signées au lieu d'utiliser des opérateurs.
Si vous êtes sur le point de faire des opérations signées uniquement et que vous n'utilisez les opérateurs que dans le code, alors vous pouvez utiliser ces types primitifs courts , int et long .
En fait word , dword et qword n'existent pas dans le langage, mais vous pouvez créer une nouvelle classe pour chacun et l'implémentation de chacun devrait être très facile:
Le mot de classe ne contient que le type primitif court , la classe dword ne contient que le type primitif int et la classe qword ne contient que le type primitif long . Maintenant, toutes les méthodes non signées et signées sont statiques ou non selon votre choix, vous pouvez implémenter dans chaque classe, c'est-à-dire toutes les opérations 16 bits à la fois non signées et signées en donnant des noms significatifs sur la classe de mots , toutes les opérations 32 bits non signées et signé en donnant des noms de signification sur la classe dword et toutes les opérations 64 bits non signées et signées en donnant des noms de signification sur la classe qword .
Si vous n'aimez pas donner trop de noms différents pour chaque méthode, vous pouvez toujours utiliser la surcharge en Java, bon de lire que Java n'a pas supprimé cela aussi!
Si vous voulez des méthodes plutôt que des opérateurs pour les opérations signées 8 bits et des méthodes pour les opérations non signées 8 bits qui n'ont aucun opérateur, vous pouvez créer la classe Byte (notez que la première lettre «B» est en majuscule, donc ce n'est pas le octet de type primitif ) et implémenter les méthodes de cette classe.
À propos du passage par valeur et du passage par référence:
Si je ne me trompe pas, comme en C #, les objets primitifs sont passés naturellement par valeur, mais les objets classe sont passés naturellement par référence, ce qui signifie que les objets de type Byte , word , dword et qword seront passés par référence et non par valeur par défaut. Je souhaite que Java ait des objets struct comme C #, afin que tous les octets , mots , dword et qword puissent être implémentés pour être struct au lieu de classe, donc par défaut, ils ont été passés par valeur et non par référence par défaut, comme tout objet struct en C #, comme les types primitifs, sont passés par valeur et non par référence par défaut, mais parce que Java est pire que C # et nous avons pour y faire face, il n'y a que des classes et des interfaces, qui sont passées par référence et non par valeur par défaut. Donc, si vous voulez passer des objets Byte , word , dword et qword par valeur et non par référence, comme tout autre objet de classe en Java et également en C #, vous devrez simplement utiliser le constructeur de copie et c'est tout.
C'est la seule solution à laquelle je peux penser. Je souhaite juste que je puisse simplement taper les types primitifs en word, dword et qword, mais Java ne prend pas en charge typedef ni n'utilise du tout, contrairement à C # qui prend en charge l' utilisation , qui est équivalent au typedef du C.
À propos de la sortie:
Pour la même séquence de bits , vous pouvez les imprimer de plusieurs façons: en binaire, en décimal (comme la signification de% u dans C printf), en octal (comme la signification de% o en C printf), en hexadécimal (comme la signification de% x dans C printf) et comme entier (comme la signification de% d dans C printf).
Notez que C printf ne connaît pas le type des variables transmises en tant que paramètres à la fonction, donc printf ne connaît le type de chaque variable que depuis l'objet char * passé au premier paramètre de la fonction.
Ainsi, dans chacune des classes: octet , mot , dword et qword , vous pouvez implémenter la méthode d'impression et obtenir les fonctionnalités de printf, même si le type primitif de la classe est signé, vous pouvez toujours l'imprimer comme non signé en suivant un algorithme impliquant opérations logiques et de décalage pour obtenir les chiffres à imprimer sur la sortie.
Malheureusement, le lien que je vous ai donné ne montre pas comment implémenter ces méthodes d'impression, mais je suis sûr que vous pouvez rechercher sur Google les algorithmes dont vous avez besoin pour implémenter ces méthodes d'impression.
C'est tout ce que je peux répondre à votre question et vous suggérer.
la source
Parce que le
unsigned
type est du mal pur.Le fait qu'en C
unsigned - int
produitunsigned
est encore plus mauvais.Voici un instantané du problème qui m'a brûlé plus d'une fois:
Avez-vous déjà remarqué le bug? J'avoue ne l'avoir vu qu'après être intervenu avec le débogueur.
Parce qu'il
n
est de type non signé,size_t
l'expression entière estn - (rays.size() - 1) / 2
évaluée commeunsigned
. Cette expression est destinée à être une position signée dun
e rayon du milieu: le 1er rayon du milieu du côté gauche aurait la position -1, le 1er du côté droit aurait la position +1, etc. Après en prenant la valeur abs et en multipliant par l'delta
angle, j'obtiendrais l'angle entren
le rayon e et celui du milieu.Malheureusement pour moi, l'expression ci-dessus contenait le mal non signé et au lieu d'évaluer, disons, -1, elle a été évaluée à 2 ^ 32-1. La conversion suivante a
double
scellé le bogue.Après un bogue ou deux causé par une mauvaise utilisation de l'
unsigned
arithmétique, il faut commencer à se demander si le bit supplémentaire que l'on obtient vaut le problème supplémentaire. J'essaie, autant que possible, d'éviter toute utilisation deunsigned
types en arithmétique, bien que je l'utilise toujours pour des opérations non arithmétiques telles que les masques binaires.la source
unsigned
est converti enint
à chaque opération, à quoi ça sertunsigned
? Il n'aura aucune fonctionnalité distincte deshort
. Et si vous vous convertissez àint
uniquement sur des opérations mixtes, telles queunsigned+int
ouunsigned+float
, vous avez toujours le problème de((unsigned)25-(unsigned)30)*1.0 > 0
, qui est une cause majeure deunsigned
bogues liés.exit(1);
«vaut vraiment la peine supplémentaire»? Le fait de ne pas pouvoir ouvrir de gros fichiers vaut-il vraiment la sécurité que les programmeurs Java moins expérimentés ne gâcheront pasunsigned
?n - (rays.size() - 1) / 2
. Vous devez toujours mettre entre crochets les opérateurs binaires car le lecteur du code ne doit pas avoir à supposer quoi que ce soit sur l'ordre des opérations dans un programme informatique. Tout simplement parce que nous disons conventionnellement a + b c = a + (b c) ne signifie pas que vous pouvez assumer cela lors de la lecture du code. De plus, le calcul doit être défini en dehors de la boucle afin qu'il puisse être testé sans la boucle présente. C'est un bug pour ne pas vous assurer que vos types s'alignent plutôt qu'un problème d'entiers non signés. En C, c'est à vous de vous assurer que vos types s'alignent.Il y a quelques joyaux dans la spécification 'C' que Java a abandonné pour des raisons pragmatiques mais qui reculent lentement avec la demande des développeurs (fermetures, etc.).
J'en mentionne une première parce qu'elle est liée à cette discussion; l'adhésion des valeurs de pointeur à l'arithmétique d'entier non signé. Et, par rapport à ce sujet de discussion, la difficulté de maintenir la sémantique non signée dans le monde signé de Java.
Je suppose que si l'on devait obtenir un alter ego de Dennis Ritchie pour informer l'équipe de conception de Gosling, il aurait suggéré de donner à Signed un "zéro à l'infini", de sorte que toutes les demandes de décalage d'adresse ajouteraient d'abord leur taille de bague algébrique pour éviter les valeurs négatives.
De cette façon, tout décalage lancé sur le tableau ne peut jamais générer de SEGFAULT. Par exemple, dans une classe encapsulée que j'appelle RingArray of doubles qui a besoin d'un comportement non signé - dans le contexte d'une "boucle auto-rotative":
Le RingArray ci-dessus n'obtiendrait jamais un index négatif, même si un demandeur malveillant tentait de le faire. N'oubliez pas qu'il existe également de nombreuses demandes légitimes pour demander des valeurs d'index antérieures (négatives).
NB: Le% module externe dé-référence les demandes légitimes tandis que le module% interne masque la malveillance flagrante des négatifs plus négatifs que le module. Si cela devait apparaître dans un Java + .. + 9 || 8 + .. + spec, alors le problème deviendrait véritablement un 'programmeur qui ne peut pas "s'auto-tourner" FAULT'.
Je suis sûr que la soi-disant `` déficience '' non signée Java peut être compensée avec le one-liner ci-dessus.
PS: Juste pour donner un contexte au ménage RingArray ci-dessus, voici une opération 'set' candidate pour correspondre à l'opération d'élément 'get' ci-dessus:
la source
Je peux penser à un effet secondaire malheureux. Dans les bases de données intégrées Java, le nombre d'ID que vous pouvez avoir avec un champ d'ID 32 bits est 2 ^ 31, pas 2 ^ 32 (~ 2 milliards, pas ~ 4 milliards).
la source
La raison pour laquelle à mon humble avis est qu'ils sont / étaient trop paresseux pour mettre en œuvre / corriger cette erreur. Le fait de suggérer que les programmeurs C / C ++ ne comprennent pas les indicateurs non signés, la structure, l'union, les bits ... est tout simplement absurde.
Ether vous parliez avec un programmeur basique / bash / java sur le point de commencer la programmation à la C, sans aucune connaissance réelle de ce langage ou vous parlez simplement de votre propre esprit. ;)
lorsque vous traitez chaque jour sur le format à partir d'un fichier ou d'un matériel, vous commencez à vous demander ce qu'ils pensaient.
Un bon exemple ici serait d'essayer d'utiliser un octet non signé comme boucle auto-rotative. Pour ceux d'entre vous qui ne comprennent pas la dernière phrase, comment diable vous appelez-vous programmeur.
DC
la source