Pourquoi Java ne prend-il pas en charge les entrées non signées?

374

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?

dsimcha
la source
137
Je ne sais pas mais ça me dérange énormément; par exemple, il est beaucoup plus difficile d'écrire du code réseau de cette façon.
Tamas Czinege
20
Je souhaite qu'il n'y ait que deux types dans le monde de la langue / base de données / ...: nombre et chaîne :)
Liao
5
Écrire du code réseau n'est pas du tout plus difficile. BTW InputStream.read (), retourne un octet non signé, pas signé par exemple, donc l'exemple de réseau est une confusion à mon humble avis. Sa seule confusion est que vous supposez que l'écriture d'une valeur signée est différente de l'écriture d'une valeur non signée. c'est-à-dire si vous ne savez pas réellement ce qui se passe au niveau des octets.
Peter Lawrey
19
@ZachSaw - J'ai également fait une double prise lorsque j'ai vu un concepteur de langage faire cette citation. Il n'y a rien de plus simple qu'un entier non signé. Les entiers signés sont compliqués. Surtout quand on considère le bit twiddling au niveau du transistor. Et comment un entier signé change-t-il? J'ai dû conclure que le concepteur de Java a un sérieux problème de compréhension de la logique booléenne.
PP.
8
Pour moi, il devient plus difficile de faire un traitement d'image avec des images qui bytene peuvent pas donner un 140niveau de gris droit mais -116dont vous avez besoin & 0xffpour obtenir la valeur correcte.
Matthieu

Réponses:

193

Ceci est tiré d'une interview avec Gosling et d'autres , sur la simplicité:

Gosling: Pour moi en tant que concepteur de langage, que je ne considère pas vraiment comme ces jours-ci, ce que "simple" a fini par signifier était que je pouvais m'attendre à ce que J. Random Developer tienne la spécification dans sa tête. Cette définition dit que, par exemple, Java n'est pas - et en fait, beaucoup de ces langages se retrouvent avec beaucoup de cas d'angle, des choses que personne ne comprend vraiment. Quiz n'importe quel développeur C sur non signé, et très vite vous découvrez que presque aucun développeur C ne comprend réellement ce qui se passe avec non signé, ce qu'est l'arithmétique non signé. Des choses comme ça ont rendu C complexe. La partie langage de Java est, je pense, assez simple. Les bibliothèques que vous devez rechercher.

Uri
la source
222
Je vais devoir être en désaccord avec Gosling ici avec un exemple spécifique (de CLR pas moins). Quoi de plus confus de donner à un tableau une valeur de longueur entière signée ou une longueur non signée? Il est impossible pour un tableau d'avoir une longueur négative, mais notre API indique que c'est possible.
JaredPar
18
L'argument de rendre Java simple fait partie de ce qui nous a mis dans le pétrin avec un manque de modèles qu'ils ont finalement introduit dans le langage parce que les alternatives étaient si lourdes. Je pense que l'on pourrait prendre en charge les entiers non signés avec une classe appropriée, mais il n'a pas besoin de prims
Uri
59
Si Java a besoin d'entiers non signés car les indices de tableau ne peuvent pas être négatifs, il a également besoin de sous-plages (à la Pascal) car un index de tableau ne peut pas être supérieur à la taille du tableau.
Wayne Conrad
81
D'accord, il vient de dire les avantages de ne pas avoir de types non signés. Maintenant, comptons les inconvénients ...
Moshe Revah
83
Je préfère la simplicité du code à la simplicité du langage. C'est pourquoi je déteste Java.
Pijusn
50

En lisant entre les lignes, je pense que la logique était quelque chose comme ceci:

  • de manière générale, les concepteurs Java souhaitaient simplifier le répertoire des types de données disponibles
  • à des fins quotidiennes, ils estiment que le besoin le plus courant concerne les types de données signés
  • pour l'implémentation de certains algorithmes, l'arithmétique non signée est parfois nécessaire, mais le type de programmeurs qui implémenteraient de tels algorithmes auraient également les connaissances nécessaires pour "contourner" l'exécution de l'arithmétique non signée avec des types de données signés

Surtout, je dirais que c'était une décision raisonnable. Eventuellement, j'aurais:

  • rendu l'octet non signé, ou au moins fourni une alternative signée / non signée, éventuellement avec des noms différents, pour ce type de données (le rendre signé est bon pour la cohérence, mais quand avez-vous besoin d'un octet signé?)
  • supprimé "short" (quand avez-vous utilisé l'arithmétique signée 16 bits pour la dernière fois?)

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.

Neil Coffey
la source
2
J'aimerais aussi avoir des octets non signés, mais je soupçonne que l'avantage d'une cohérence complète entre les types entiers l'emporte sur la commodité que les octets non signés apporteraient.
Alan Moore
64
"Pour les besoins quotidiens, ils ont estimé que le besoin le plus courant était pour les types de données signés". Dans mon code C ++, je me retrouve le plus souvent à penser "Pourquoi diable est-ce que j'utilise ici un entier signé au lieu d'un entier non signé?!". J'ai le sentiment que "signé" est l'exception plutôt que la règle (bien sûr, cela dépend du domaine, mais il y a une raison pour laquelle les entiers positifs sont appelés nombres naturels ;-)).
Luc Touraille
15
pouce levé pour l'appel d'octets non signés, lors du traitement d'image, en supposant que les octets ne sont pas signés (comme il se doit), cela m'a fait passer des heures à déboguer.
Helin Wang
7
vous seriez surpris de la fréquence d' shortutilisation - les algorithmes defltate / gzip / inflate sont en 16 bits et ils reposent fortement sur des courts métrages ... ou du moins short[][certes, ils sont natifs - pourtant, l'impl java de l'algorithme contient des terrabytes de données]. Ce dernier ( short[]) a un avantage significatif int[]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.
bestsss
8
Bien que dans une application particulière, vous devez mesurer si l'utilisation de courts métrages vous donne de meilleures performances plutôt que de supposer qu'elle est vraie. Il est possible que le jiggery-pokery supplémentaire requis pour manipuler les shorts plutôt que les entiers (qui est généralement le type que le processeur `` aime utiliser '') puisse en fait nuire aux performances dans une application particulière. Pas toujours, mais il faut tester, pas assumer.
Neil Coffey
19

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 bits

short - Entier signé 16 bits

int - Entier signé 32 bits

long - Entier signé 64 bits

char - Caractère 16 bits (entier non signé)

Bien charqu'il ne prenne pas en charge l' unsignedarithmétique, il peut essentiellement être traité comme un unsignedentier. Vous devrez reconstituer explicitement les opérations arithmétiques char, mais cela vous permet de spécifier des unsignednombres.

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--;

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 Longet Integerqui fournissent des méthodes d'assistance lors du traitement longet des intvaleurs 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 unsignedentiers.

Jyro117
la source
2
Mais cependant, il charest trop petit pour prendre en charge l' longarithmétique, par exemple.
3
Cela pourrait être un inconvénient de Java
En espérant qu'ils prennent en charge les valeurs non signées pour les octets. Rend les choses plus faciles.
mixturez
15

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.

tapoter
la source
37
Ouais, je suis sûr que Gosling est très ignorant de Java par rapport à vous.
jakeboxer
Java permet-il d'effectuer l'arithmétique directement sur des quantités d'octets non signés, ou les valeurs sont-elles toujours promues? Avoir un type non signé pour le stockage, mais toujours effectuer des opérations arithmétiques sur un type signé qui est assez grand pour l'adapter fonctionne bien sémantiquement, mais entraînerait des opérations plus coûteuses sur les types non signés de la même taille que les entiers "normaux".
supercat
2
C'est un mauvais style à utiliser charpour autre chose que pour les personnages.
starblue
5
@starblue Bien sûr que oui, mais c'est un hack pour contourner une limitation de la langue
Basic
14

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.

Bombe
la source
12
Quant au mélange signé / non signé: vous pouvez avoir des types non signés, mais interdire le mélange (ou exiger des transtypages explicites). Je ne sais toujours pas si c'est nécessaire.
sleske
2
En C ++, vous devez saupoudrer static_castbeaucoup pour les mélanger. C'est en effet désordonné.
Raedwald
4
Le 8ème bit est là, il essaie juste de se cacher comme signe.
starblue
Les choses ne deviennent désordonnées qu'avec les types 32 bits ou plus. Je ne vois aucune raison pour laquelle Java n'aurait pas dû byteêtre signé comme c'était le cas en Pascal.
supercat
12
Venez me voir lorsque vous rencontrez des problèmes avec le traitement d'image en Java, où vous vous attendez à ce que les octets soient non signés. Ensuite, vous saurez que & 0xFFchaque promotion d'octet à entier rend le code encore plus compliqué.
bit2shift
12

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.

akatakritos
la source
34
Les entiers signés Java se déplacent également. Je ne vois pas votre point.
foo
8
@foo: Les entiers signés doivent devenir gros avant de causer des problèmes. En revanche, en C, on peut avoir des problèmes à comparer n'importe quel entier négatif - même - à -1n'importe quelle quantité non signée - même zéro.
supercat
Il est dommage que Java n'ait pas pu inclure de types non signés, mais avec un ensemble limité de conversions et d'opérateurs mixtes (quelque peu analogue à la façon dont en C on peut ajouter 5 à un pointeur, mais on ne peut pas comparer un pointeur à 5) . L'idée que l'utilisation d'un opérateur sur des types mixtes lorsqu'une conversion implicite existe, devrait forcer l'utilisation implicite de cette conversion (et utiliser le type conséquent comme type de résultat) est au cœur de nombreuses décisions de conception douteuses à la fois dans .NET et Java.
supercat
4
Ne pas se plaindre de votre réponse, mais avoir un -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 ±26qui est tout simplement fausse. La bonne gestion des valeurs inconnues est une sorte de Option<TArg>quand Some(25) - Nonereviendrait None.
bytebuster
11

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, utilisez long. S'ils ne correspondent pas à l' longutilisation BigInteger.

  • 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/ byteet 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".

starblue
la source
5
Oui, pour passer à droite, vous devez choisir entre >>et >>>pour signé et non signé, respectivement. Décaler à gauche n'est pas un problème.
starblue
1
@starblue En >>>fait, ne fonctionne pas pour shortet byte. Par exemple, les (byte)0xff>>>1rendements 0x7fffffffplutôt que 0x7f. Un autre exemple: byte b=(byte)0xff; b>>>=1;entraînera b==(byte)0xff. Bien sûr, vous pouvez le faire, b=(byte)(b & 0xff >> 1);mais cela ajoute une opération de plus (bit à bit &).
CITBL
7
"... Même avec le modèle simplifié, la plupart des programmeurs Java ne savent pas comment se comportent les types numériques de base ..." Quelque chose en moi ne me fait ressentir qu'un langage visant le plus petit dénominateur commun.
Basic
La première ligne de votre réponse, à propos de plus de complications et de peu de gains, est précisément ce que j'ai développé dans mon article 6 ans plus tard: nayuki.io/page/unsigned-int-considered-harmful-for-java
Nayuki
1
@Nayuki Votre article est vraiment sympa. Seule une petite remarque, j'utiliserais l'ajout de 0x80000000 pour les opérateurs de comparaison au lieu de XOR, car cela explique pourquoi cela fonctionne, il déplace la région contiguë où la comparaison se produit de -MAXINT à 0. Au niveau du bit, son effet est exactement le même.
starblue
6

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.

John Hascall
la source
12
aka "Donc les gens l'utilisent vraiment et nous avons eu tort de ne pas l'inclure pour commencer" - mais nous ne faisons toujours pas vraiment confiance aux développeurs Java pour savoir si une variable est signée ou non - donc nous n'allons pas les implémenter dans la machine virtuelle ou en tant que types équivalents à leurs cousins ​​signés.
Basic
6

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 inttype 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 la Integerclasse pour utiliser intle type de données comme un entier non signé et des méthodes statiques comme compareUnsigned(), divideUnsigned()etc. ont été ajoutées à la Integerclasse pour prendre en charge les opérations arithmétiques pour les entiers non signés.

Morteza Adi
la source
4

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.

Rob Ottaway
la source
Ce serait bien ... sauf que les preuves de l'interview de Gosling impliquent que des entiers non signés (à part 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.
Stephen C
C'est une bonne idée de ne jamais mettre trop de valeur dans les déclarations de témoins oculaires, si des preuves documentaires sont également à portée de main.
user7610
4

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.

Jonathan
la source
2

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
MASM (assembleur Microsoft) et Windows définissent BYTE, WORD, DWORD, QWORD comme des types non signés. Pour MASM, SBYTE, SWORD, SDWORD, SQWORD sont les types signés.
rcgldr
1

Parce que le unsignedtype est du mal pur.

Le fait qu'en C unsigned - intproduit unsignedest encore plus mauvais.

Voici un instantané du problème qui m'a brûlé plus d'une fois:

// 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 ...
}

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 nest de type non signé, size_tl'expression entière est n - (rays.size() - 1) / 2évaluée comme unsigned. Cette expression est destinée à être une position signée du ne 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' deltaangle, j'obtiendrais l'angle entre nle 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 doublescellé le bogue.

Après un bogue ou deux causé par une mauvaise utilisation de l' unsignedarithmé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 de unsignedtypes en arithmétique, bien que je l'utilise toujours pour des opérations non arithmétiques telles que les masques binaires.

Michael
la source
Ajouter "unsigned long" à Java serait gênant. L'ajout de types non signés plus petits, cependant, n'aurait pas dû poser de problème. En particulier, les types plus petits que "int" auraient pu être facilement gérés en les faisant passer à "int" de manière évidente numériquement, et "unsigned int" aurait pu être traité en disant que les opérations impliquant un int signé et un int non signé favoriseraient les deux opérandes à "long". La seule situation problématique serait les opérations impliquant une quantité longue non signée et une quantité signée, car il n'y aurait aucun type capable de représenter toutes les valeurs des deux opérandes.
supercat
@supercat: si unsignedest converti en intà chaque opération, à quoi ça sert unsigned? Il n'aura aucune fonctionnalité distincte de short. Et si vous vous convertissez à intuniquement sur des opérations mixtes, telles que unsigned+intou unsigned+float, vous avez toujours le problème de ((unsigned)25-(unsigned)30)*1.0 > 0, qui est une cause majeure de unsignedbogues liés.
Michael
De nombreuses opérations sur des types non signés se transformeraient en "long". Exiger des transtypages explicites lors du stockage du résultat sur des types non signés provoquerait à peu près les mêmes ennuis que ceux qui existent avec les octets courts et les octets, mais si le type est principalement un format de stockage plutôt qu'un format de calcul, cela ne devrait pas être un problème. Dans tous les cas, les types non signés plus courts que "int" devraient simplement pouvoir passer à "int" sans difficulté.
supercat
3
Je n'aime pas cette réponse car elle utilise l'argument "les entiers non signés sont mauvais et ne devraient pas exister car ils ne peuvent jamais être signés". Quiconque essaie de soustraire un entier non signé devrait déjà le savoir. Et quant à la lisibilité, C n'est pas exactement connu pour être facile à suivre. De plus, l'argument (semi-) "le bit supplémentaire ne vaut pas la peine supplémentaire" est également très faible. La gestion des erreurs au lieu de 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 pas unsigned?
yyny
2
La seule mauvaise chose que je vois dans ce code est 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.
Dmitry
0

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":

// ...
// 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
// ...

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:

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
la source
-2

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).

mike g
la source
1
Il pense probablement aux tableaux et ne peut pas utiliser des entiers négatifs comme indices. Probablement.
SK9
2
Lorsque les champs d'incrémentation automatique dans les bases de données débordent, ils deviennent souvent fous.
Joshua
-8

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

Denis Co
la source
34
Juste pour les coups de pied, Google l'expression «boucle auto-rotative». De toute évidence , Denis Co est la seule personne au monde digne de s'appeler programmeur :-)
Stephen C
6
Cette réponse est si mauvaise que c'est drôle
Nayuki